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

Commit d17cf59

Browse files
authored
Merge pull request #461 from topcoder-platform/develop
Version 1.2.2
2 parents c30fa03 + c92f485 commit d17cf59

25 files changed

+547
-94
lines changed

Diff for: .DS_Store

0 Bytes
Binary file not shown.

Diff for: .circleci/config.yml

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,25 @@ install_dependency: &install_dependency
88
sudo apt install jq
99
sudo pip install awscli --upgrade
1010
sudo pip install docker-compose
11+
no_output_timeout: 30m
12+
13+
1114
install_deploysuite: &install_deploysuite
1215
name: Installation of install_deploysuite.
1316
command: |
1417
git clone --branch v1.4.1 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript
1518
cp ./../buildscript/master_deploy.sh .
1619
cp ./../buildscript/buildenv.sh .
1720
cp ./../buildscript/awsconfiguration.sh .
21+
no_output_timeout: 30m
22+
23+
build_app: &build_app
24+
name: Build the app
25+
command: |
26+
./build.sh
27+
no_output_timeout: 30m
28+
29+
1830
restore_cache_settings_for_build: &restore_cache_settings_for_build
1931
key: docker-node-modules-{{ checksum "package-lock.json" }}
2032

@@ -29,7 +41,7 @@ builddeploy_steps: &builddeploy_steps
2941
- run: *install_dependency
3042
- run: *install_deploysuite
3143
- restore_cache: *restore_cache_settings_for_build
32-
- run: ./build.sh
44+
- run: *build_app
3345
- save_cache: *save_cache_settings
3446
- deploy:
3547
name: Running MasterScript.
@@ -39,6 +51,7 @@ builddeploy_steps: &builddeploy_steps
3951
./buildenv.sh -e $DEPLOY_ENV -b ${DEPLOY_ENV}-${APPNAME}-deployvar
4052
source buildenvvar
4153
./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${DEPLOY_ENV}-global-appvar,${DEPLOY_ENV}-${APPNAME}-appvar -i ${APPNAME}
54+
4255

4356

4457
jobs:
@@ -66,7 +79,7 @@ workflows:
6679
context : org-global
6780
filters:
6881
branches:
69-
only: [develop, "feature/Auth0-RS256-Token"]
82+
only: [develop, "issue_443"]
7083

7184
# Production builds are exectuted only on tagged commits to the
7285
# master branch.

Diff for: configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The following config parameters are supported, they are defined in `src/config.j
3535
|AWS_CONNECTION_TIMEOUT | The timeout used to check if the app is healthy. |10000 |
3636
|TC_LOGIN_URL | TC login url | |
3737
|DYNAMODB_WAIT_TABLE_FOR_ACTIVE_TIMEOUT | Dynamodb wait for active timeout |10 minutes |
38+
|TC_API_V5_URL | Topcoder API v5 url for retrieving list of Connect Projects | |
3839

3940

4041
## GitHub OAuth App Setup

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"superagent-promise": "^1.1.0",
8888
"typescript": "~2.3.3",
8989
"uuid": "^3.3.2",
90+
"ui-select": "~0.19.8",
9091
"winston": "^2.3.1",
9192
"tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.1"
9293
},

Diff for: src/common/constants.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ const GITLAB_MAX_PER_PAGE = Number.MAX_SAFE_INTEGER;
4343
// the access level can be: 10 - GUEST, 20 - REPORTER, 30 - DEVELOPER, 40 - MASTER, 50 - OWNER
4444
const GITLAB_DEFAULT_GROUP_ACCESS_LEVEL = 30;
4545

46-
// The Gitlab access token default expiration in seconds
47-
const GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION = 3600 * 24 * 14;
46+
// The Gitlab access token default expiration in seconds (2 hours expiration)
47+
const GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION = 3600 * 2;
4848

49-
// The Gitlab refresh token time in seconds before expiration
49+
// The Gitlab refresh token time in seconds before expiration (5 minute before expiration)
5050
const GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION = 300;
5151

5252
const GITHUB_OWNER_CALLBACK_URL = '/api/v1/github/owneruser/callback';

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

+94-3
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,43 @@ async function queryOneIssue(model, repositoryId, number, provider) {
179179
});
180180
}
181181

182+
/**
183+
* Get Issue's id and challengeUUID by repoUrl
184+
* @param {String} repoUrl The repo url
185+
* @returns {Promise<Object>}
186+
*/
187+
async function queryIssueIdChallengeUUIDByRepoUrl(repoUrl) {
188+
return await new Promise((resolve, reject) => {
189+
models.Issue.scan('repoUrl').eq(repoUrl)
190+
.attributes(['id', 'challengeUUID'])
191+
.exec((err, result) => {
192+
if (err) {
193+
return reject(err);
194+
}
195+
return resolve(result);
196+
});
197+
});
198+
}
199+
200+
201+
/**
202+
* Get CopilotPayment's id by challengeUUID
203+
* @param {String} challengeUUID The challengeUUID
204+
* @returns {Promise<String>}
205+
*/
206+
async function queryPaymentIdByChallengeUUID(challengeUUID) {
207+
return await new Promise((resolve, reject) => {
208+
models.CopilotPayment.scan('challengeUUID').eq(challengeUUID)
209+
.attributes(['id'])
210+
.exec((err, result) => {
211+
if (err) {
212+
return reject(err);
213+
}
214+
return resolve(result.id);
215+
});
216+
});
217+
}
218+
182219
/**
183220
* Get single data by query parameters
184221
* @param {Object} model The dynamoose model to query
@@ -248,6 +285,27 @@ async function queryOneUserMappingByTCUsername(model, tcusername) {
248285
});
249286
}
250287

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+
251309
/**
252310
* Get single data by query parameters
253311
* @param {Object} model The dynamoose model to query
@@ -257,7 +315,7 @@ async function queryOneUserMappingByTCUsername(model, tcusername) {
257315
async function queryOneActiveProject(model, repoUrl) {
258316
return await new Promise((resolve, reject) => {
259317
queryOneActiveRepository(models.Repository, repoUrl).then((repo) => {
260-
if (!repo) resolve(null);
318+
if (!repo || repo.length === 0) resolve(null);
261319
else model.queryOne('id').eq(repo.projectId).consistent()
262320
.exec((err, result) => {
263321
if (err) {
@@ -470,6 +528,35 @@ async function queryOneOrganisation(model, organisation) {
470528
});
471529
}
472530

531+
/**
532+
* Query one active repository
533+
* @param {String} url the repository url
534+
* @returns {Promise<Object>}
535+
*/
536+
async function queryOneRepository(url) {
537+
return await new Promise((resolve, reject) => {
538+
models.Repository.query({
539+
url,
540+
})
541+
.all()
542+
.exec((err, repos) => {
543+
if (err) {
544+
return reject(err);
545+
}
546+
if (!repos || repos.length === 0) resolve(null);
547+
if (repos.length > 1) {
548+
let error = `Repository's url is unique in this version.
549+
This Error must be caused by old data in the Repository table.
550+
The old version can only guarrentee that the active Repository's url is unique.
551+
Please migrate the old Repository table.`;
552+
logger.debug(`queryOneRepository. Error. ${error}`);
553+
reject(error);
554+
}
555+
return resolve(repos[0]);
556+
});
557+
});
558+
}
559+
473560
/**
474561
* Query one active repository
475562
* @param {Object} model the dynamoose model
@@ -480,8 +567,8 @@ async function queryOneActiveRepository(model, url) {
480567
return await new Promise((resolve, reject) => {
481568
model.queryOne({
482569
url,
483-
archived: 'false'
484570
})
571+
.filter('archived').eq('false')
485572
.all()
486573
.exec((err, result) => {
487574
if (err) {
@@ -502,8 +589,8 @@ async function queryActiveRepositoriesExcludeByProjectId(url, projectId) {
502589
return await new Promise((resolve, reject) => {
503590
models.Repository.query({
504591
url,
505-
archived: 'false'
506592
})
593+
.filter('archived').eq('false')
507594
.filter('projectId')
508595
.not().eq(projectId)
509596
.all()
@@ -580,6 +667,8 @@ async function populateRepoUrls(projectId) {
580667
}
581668

582669
module.exports = {
670+
queryIssueIdChallengeUUIDByRepoUrl,
671+
queryPaymentIdByChallengeUUID,
583672
getById,
584673
getByKey,
585674
scan,
@@ -597,13 +686,15 @@ module.exports = {
597686
queryOneActiveProject,
598687
queryOneActiveProjectWithFilter,
599688
queryOneActiveRepository,
689+
queryOneRepository,
600690
queryOneOrganisation,
601691
queryOneIssue,
602692
queryOneUserByType,
603693
queryOneUserByTypeAndRole,
604694
queryOneUserGroupMapping,
605695
queryOneUserTeamMapping,
606696
queryOneUserMappingByTCUsername,
697+
queryTCUsernameByGitUsername,
607698
queryRepositoriesByProjectId,
608699
queryRepositoryByProjectIdFilterUrl
609700
};

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/config.js

+10
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,14 @@ module.exports.frontendConfigs = {
8383
TOPCODER_URL: process.env.TOPCODER_URL || 'https://topcoder-dev.com',
8484
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || 'https://github.com/orgs/',
8585
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || 'https://gitlab.com/groups/',
86+
TC_API_V5_URL: process.env.TC_API_V5_URL || 'https://api.topcoder-dev.com/v5',
87+
TOPCODER_VALUES: {
88+
dev: {
89+
TC_API_V4_URL: process.env.TC_API_V4_URL || 'https://api.topcoder-dev.com/v4',
90+
},
91+
prod: {
92+
TC_API_V4_URL: process.env.TC_API_V4_URL || 'https://api.topcoder.com/v4',
93+
},
94+
},
95+
TOPCODER_ENV: process.env.TOPCODER_ENV || 'dev',
8696
};

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) {

0 commit comments

Comments
 (0)