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

Commit 9935ebb

Browse files
committed
https://github.com/topcoder-platform/topcoder-x-ui/issues/459
1 parent aec774d commit 9935ebb

File tree

8 files changed

+125
-31
lines changed

8 files changed

+125
-31
lines changed

Diff for: src/common/db-helper.js

+22
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,27 @@ async function queryOneUserMappingByTCUsername(model, tcusername) {
285285
});
286286
}
287287

288+
/**
289+
* Get single data by query parameters
290+
* @param {Object} model The dynamoose model to query
291+
* @param {String} provider The git provider
292+
* @param {String} gitUsername The git username
293+
* @returns {Promise<void>}
294+
*/
295+
async function queryTCUsernameByGitUsername(model, provider, gitUsername) {
296+
return await new Promise((resolve, reject) => {
297+
model.queryOne(`${provider}Username`).eq(gitUsername)
298+
.all()
299+
.exec((err, result) => {
300+
if (err) {
301+
logger.debug(`queryTCUsernameByGitUsername. Error. ${err}`);
302+
return reject(err);
303+
}
304+
return resolve(result.topcoderUsername);
305+
});
306+
});
307+
}
308+
288309
/**
289310
* Get single data by query parameters
290311
* @param {Object} model The dynamoose model to query
@@ -673,6 +694,7 @@ module.exports = {
673694
queryOneUserGroupMapping,
674695
queryOneUserTeamMapping,
675696
queryOneUserMappingByTCUsername,
697+
queryTCUsernameByGitUsername,
676698
queryRepositoriesByProjectId,
677699
queryRepositoryByProjectIdFilterUrl
678700
};

Diff for: src/common/helper.js

+55-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const bcrypt = require('bcryptjs');
1919
const moment = require('moment');
2020
const parseDomain = require('parse-domain');
2121
const config = require('../config');
22+
const kafka = require('../utils/kafka');
23+
const models = require('../models');
2224
const logger = require('./logger');
2325
const errors = require('./errors');
2426
const constants = require('./constants');
@@ -120,6 +122,52 @@ function buildController(controller) {
120122
});
121123
}
122124

125+
/**
126+
* Convert github api error.
127+
* @param {String} copilotHandle the copilot handle
128+
* @param {String} provider the git provider
129+
*/
130+
async function sendTokenExpiredEvent(copilotHandle, provider) {
131+
const notificationTokenExpiredEvent = {
132+
event: 'notification.tokenExpired',
133+
data: {
134+
copilotHandle,
135+
provider,
136+
},
137+
};
138+
await kafka.send(JSON.stringify(notificationTokenExpiredEvent));
139+
}
140+
141+
/**
142+
* Convert github api error.
143+
* @param {Error} err the github api error
144+
* @param {String} message the error message
145+
* @param {String} gitUsername the git username
146+
* @returns {Error} converted error
147+
*/
148+
async function convertGitHubErrorAsync(err, message, gitUsername) {
149+
if (err.statusCode === 401 && gitUsername) { // eslint-disable-line no-magic-numbers
150+
const copilotHandle = await dbHelper.queryTCUsernameByGitUsername(models.GithubUserMapping, 'github', gitUsername);
151+
await sendTokenExpiredEvent(copilotHandle, 'Github');
152+
}
153+
return convertGitHubError(err, message);
154+
}
155+
156+
/**
157+
* Convert gitlab api error.
158+
* @param {Error} err the gitlab api error
159+
* @param {String} message the error message
160+
* @param {String} gitUsername the git username
161+
* @returns {Error} converted error
162+
*/
163+
async function convertGitLabErrorAsync(err, message, gitUsername) {
164+
if (err.statusCode === 401 && gitUsername) { // eslint-disable-line no-magic-numbers
165+
const copilotHandle = await dbHelper.queryTCUsernameByGitUsername(models.GitlabUserMapping, 'gitlab', gitUsername);
166+
await sendTokenExpiredEvent(copilotHandle, 'Gitlab');
167+
}
168+
return convertGitLabError(err, message);
169+
}
170+
123171
/**
124172
* Convert github api error.
125173
* @param {Error} err the github api error
@@ -209,24 +257,23 @@ async function getProviderType(repoUrl) {
209257

210258
/**
211259
* gets the git username of copilot/owner for a project
212-
* @param {Object} models the db models
213260
* @param {Object} project the db project detail
214261
* @param {String} provider the git provider
215262
* @param {Boolean} isCopilot if true, then get copilot, otherwise get owner
216263
* @returns {Object} the owner/copilot for the project
217264
*/
218-
async function getProjectCopilotOrOwner(models, project, provider, isCopilot) {
265+
async function getProjectCopilotOrOwner(project, provider, isCopilot) {
219266
const userMapping = await dbHelper.queryOneUserMappingByTCUsername(
220-
provider === 'github' ? models.GithubUserMapping : models.GitlabUserMapping,
267+
provider === 'github' ? models.GithubUserMapping : models.GitlabUserMapping,
221268
isCopilot ? project.copilot : project.owner);
222269

223-
if (!userMapping ||
224-
(provider === 'github' && !userMapping.githubUserId)
270+
if (!userMapping ||
271+
(provider === 'github' && !userMapping.githubUserId)
225272
|| (provider === 'gitlab' && !userMapping.gitlabUserId)) {
226273
throw new Error(`Couldn't find ${isCopilot ? 'copilot' : 'owner'} username for '${provider}' for this repository.`);
227274
}
228275

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

@@ -270,6 +317,8 @@ module.exports = {
270317
buildController,
271318
convertGitHubError,
272319
convertGitLabError,
320+
convertGitHubErrorAsync,
321+
convertGitLabErrorAsync,
273322
ensureExists,
274323
ensureExistsWithKey,
275324
generateIdentifier,

Diff for: src/controllers/GithubController.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ async function addUserToTeamCallback(req, res) {
160160
const token = result.body.access_token;
161161

162162
// get team details
163-
const teamDetails = await GithubService.getTeamDetails(team.ownerToken, team.teamId);
163+
const teamDetails = await GithubService.getTeamDetails(team.ownerUsername, team.ownerToken, team.teamId);
164164
const organisation = teamDetails.organization.login;
165165

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

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

@@ -247,7 +248,8 @@ async function deleteUsersFromTeam(req, res) {
247248
});
248249
// eslint-disable-next-line no-restricted-syntax
249250
for (const userTeamMapItem of userTeamMappings) {
250-
await GithubService.deleteUserFromGithubTeam(token, teamId, githubOrgId, userTeamMapItem.githubUserName);
251+
await GithubService.deleteUserFromGithubTeam(
252+
teamInDB.ownerUsername, token, teamId, githubOrgId, userTeamMapItem.githubUserName);
251253
await dbHelper.removeById(UserTeamMapping, userTeamMapItem.id);
252254
}
253255
} catch (err) {

Diff for: src/controllers/GitlabController.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,14 @@ async function ownerUserLoginCallback(req, res) {
9999
*/
100100
async function listOwnerUserGroups(req) {
101101
const user = await UserService.getAccessTokenByHandle(req.currentUser.handle, constants.USER_TYPES.GITLAB);
102+
// NOTE: Only user with topcoder-x account can pass this condition.
103+
// Only them will be inserted into `User` table,
104+
// normal user will not be in the `User` table.
102105
if (!user || !user.accessToken) {
103106
throw new errors.UnauthorizedError('You have not setup for Gitlab.');
104107
}
105108
const refreshedUser = await GitlabService.refreshGitlabUserAccessToken(user);
106-
return await GitlabService.listOwnerUserGroups(refreshedUser.accessToken, req.query.page,
109+
return await GitlabService.listOwnerUserGroups(refreshedUser.username, refreshedUser.accessToken, req.query.page,
107110
req.query.perPage, req.query.getAll);
108111
}
109112

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

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

206209
// add user to group
207210
const gitlabUser = await GitlabService.addGroupMember(
211+
refreshedOwnerUser.username,
208212
group.groupId,
209213
refreshedOwnerUser.accessToken,
210214
token,
@@ -272,8 +276,8 @@ async function deleteUsersFromTeam(req, res) {
272276
const userGroupMappings = await dbHelper.scan(UserGroupMapping, {groupId});
273277
// eslint-disable-next-line no-restricted-syntax
274278
for (const userGroupMapItem of userGroupMappings) {
275-
await GitlabService.deleteUserFromGitlabGroup(refreshedOwnerUser.accessToken, groupId,
276-
userGroupMapItem.gitlabUserId);
279+
await GitlabService.deleteUserFromGitlabGroup(refreshedOwnerUser.username,
280+
refreshedOwnerUser.accessToken, groupId, userGroupMapItem.gitlabUserId);
277281
await dbHelper.removeById(UserGroupMapping, userGroupMapItem.id);
278282
}
279283
} catch (err) {

Diff for: src/services/GithubService.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,14 @@ getTeamRegistrationUrl.schema = Joi.object().keys({
187187

188188
/**
189189
* Add team member.
190+
* @param {String} gitUsername the git username
190191
* @param {String} teamId the team id
191192
* @param {String} ownerUserToken the owner user token
192193
* @param {String} normalUserToken the normal user token
193194
* @param {String} accessLevel the team's access level
194195
* @returns {Promise} the promise result
195196
*/
196-
async function addTeamMember(teamId, ownerUserToken, normalUserToken, accessLevel) {
197+
async function addTeamMember(gitUsername, teamId, ownerUserToken, normalUserToken, accessLevel) {
197198
let username;
198199
let id;
199200
let state;
@@ -220,14 +221,15 @@ async function addTeamMember(teamId, ownerUserToken, normalUserToken, accessLeve
220221
}).get('true')
221222
.isUndefined()
222223
.value()) {
223-
throw helper.convertGitHubError(err, 'Failed to add team member');
224+
throw await helper.convertGitHubErrorAsync(err, 'Failed to add team member', gitUsername);
224225
}
225226
}
226227
// return github username and its state
227228
return {username, id, state};
228229
}
229230

230231
addTeamMember.schema = Joi.object().keys({
232+
gitUsername: Joi.string().required(),
231233
teamId: Joi.string().required(),
232234
ownerUserToken: Joi.string().required(),
233235
normalUserToken: Joi.string().required(),
@@ -342,12 +344,13 @@ getUserIdByUsername.schema = Joi.object().keys({
342344
/**
343345
* Get team detailed data
344346
*
347+
* @param {String} gitUsername git username
345348
* @param {String} token user owner token
346349
* @param {String|Number} teamId team id
347350
*
348351
* @returns {Object} team object, see https://developer.github.com/v3/teams/#get-team
349352
*/
350-
async function getTeamDetails(token, teamId) {
353+
async function getTeamDetails(gitUsername, token, teamId) {
351354
const teamIdAsNumber = !_.isNumber(teamId) ? parseInt(teamId, 10) : teamId;
352355
let team;
353356

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

358361
team = teamResponse.data;
359362
} catch (err) {
360-
throw helper.convertGitHubError(err, `Failed to get team with id '${teamId}'.`);
363+
throw await helper.convertGitHubErrorAsync(err, `Failed to get team with id '${teamId}'.`, gitUsername);
361364
}
362365

363366
return team;
364367
}
365368

366369
getTeamDetails.schema = Joi.object().keys({
370+
gitUsername: Joi.string().required(),
367371
token: Joi.string().required(),
368372
teamId: Joi.alternatives().try(Joi.string(), Joi.number()).required(),
369373
});
@@ -372,14 +376,15 @@ getTeamDetails.schema = Joi.object().keys({
372376
/**
373377
* Get team detailed data
374378
*
379+
* @param {String} gitUsername git username
375380
* @param {String} token user owner token
376381
* @param {String|Number} teamId team id
377382
* @param {String|Number} orgId team id
378383
* @param {String|Number} githubUserName team id
379384
*
380385
* @returns {Object} status object, see https://developer.github.com/v3/teams/members/#remove-team-membership
381386
*/
382-
async function deleteUserFromGithubTeam(token, teamId, orgId, githubUserName) {
387+
async function deleteUserFromGithubTeam(gitUsername, token, teamId, orgId, githubUserName) {
383388
const teamIdAsNumber = !_.isNumber(teamId) ? parseInt(teamId, 10) : teamId;
384389
let deleteResult;
385390
try {
@@ -388,12 +393,15 @@ async function deleteUserFromGithubTeam(token, teamId, orgId, githubUserName) {
388393
const deleteGithubUserEndpoint = `/organizations/${orgId}/team/${teamIdAsNumber}/memberships/${githubUserName}`;
389394
deleteResult = await team._request('DELETE', deleteGithubUserEndpoint);
390395
} catch (err) {
391-
throw helper.convertGitHubError(err, `Failed to delete user '${githubUserName}' from org with orgId '${orgId}' and team id '${teamId}'.`);
396+
throw await helper.convertGitHubErrorAsync(
397+
err, `Failed to delete user '${githubUserName}' from org with orgId '${orgId}' and team id '${teamId}'.`,
398+
githubUserName);
392399
}
393400
return deleteResult;
394401
}
395402

396403
deleteUserFromGithubTeam.schema = Joi.object().keys({
404+
gitUsername: Joi.string().required(),
397405
token: Joi.string().required(),
398406
teamId: Joi.alternatives().try(Joi.string(), Joi.number()).required(),
399407
orgId: Joi.string().required(),

0 commit comments

Comments
 (0)