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

Commit 88957c9

Browse files
author
James Cori
committed
Merge branch 'develop'
2 parents dbc2810 + fb55cbb commit 88957c9

13 files changed

+151
-26
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,12 @@ Direct ID migration can be done with script located in **topcoder-x-processor**
9393
```shell
9494
npm run direct-connect-migration
9595
```
96+
By default it takes 15 projects at time, but you can change this by specifying BATCH_SIZE environment variable.
97+
98+
## Repository url collisions scan
99+
100+
To scan and log which projects has colliding (the same) repository urls run
101+
```shell
102+
npm run log-repository-collisions
103+
```
96104
By default it takes 15 projects at time, but you can change this by specifying BATCH_SIZE environment variable.

TeamsVsGroups.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
## Github's Teams and Gitlab's Groups differences
3+
4+
Github's Teams are groups of organization members whereas Gitlab's Groups are just groups of Gitlab users (Groups are more like Github' organizations).
5+
Each Github organization can have repositories and assign teams to them and each team can have subteams.
6+
Gitlab groups can have repositories and create subgroups (nested).
7+
8+
## Setup guide
9+
10+
### Github
11+
12+
1. Go to 'https://github.com/settings/organizations' and click 'New organization' button
13+
2. Select your plan.
14+
3. Enter organization's name, contact email address, solve the captcha and click Next
15+
4. Click 'Skip this step'
16+
5. If you receive a survey, you can just go to the bottom and click Submit without filling anything.
17+
6. On your new organization page go to 'Teams' tab and click 'New team' button.
18+
7. Fill in your team's name, description (optional) and visibility. Submit by clicking 'Create team'.
19+
Now you have your team created and you should get redirect to its page.
20+
You can assign it to an organization's repository by clicking 'Add Repository' and entering repository's name, in 'Repositories' tab of a team's page.
21+
22+
### Gitlab
23+
24+
1. Go to 'https://gitlab.com/dashboard/groups' and click on 'New group' button.
25+
2. Enter group's name and set visibility level. Finish by clicking 'Create group'.
26+
You can now create repositories for this group or subgroups.
27+
28+
## Roles
29+
30+
In Topcoder X you can select role which user who joins via specific invitation link receives.
31+
32+
### Github
33+
34+
For github team you can set two roles: Member and Maintainer.
35+
You can read about them here: https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/permission-levels-for-an-organization and https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/giving-team-maintainer-permissions-to-an-organization-member
36+
37+
### Gitlab
38+
39+
For gitlab group you can set five roles: Guest, Reporter, Developer, Maintainer, Owner
40+
You can read about them here: https://docs.gitlab.com/ee/user/permissions.html

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"test:fe": "gulp protractor",
2222
"lint": "gulp lint",
2323
"heroku-postbuild": "gulp build",
24-
"create-tables": "CREATE_DB=true node scripts/create-update-tables.js"
24+
"create-tables": "CREATE_DB=true node scripts/create-update-tables.js",
25+
"log-repository-collisions": "node scripts/log-repository-collisions.js"
2526
},
2627
"dependencies": {
2728
"angular": "~1.8.0",

scripts/log-repository-collisions.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const logger = require('../src/common/logger')
2+
const models = require('../src/models')
3+
const _ = require('lodash')
4+
5+
async function main() {
6+
const BATCH_SIZE = process.env.BATCH_SIZE || 15;
7+
let previousSize = BATCH_SIZE;
8+
let previousKey = null;
9+
// Array containing project ids already found colliding
10+
const collidingUrls = []
11+
let batch = 1;
12+
// Run this loop as long as there can be more objects in a database
13+
while (previousSize === BATCH_SIZE) {
14+
logger.debug(`Running batch no. ${batch}`)
15+
// Go over all active projects, limiting to BATCH_SIZE
16+
const projects = await models.Project.scan({
17+
archived: 'false'
18+
}).consistent().limit(BATCH_SIZE).startAt(previousKey).exec()
19+
for (const project of projects) {
20+
// If url was already found colliding go to a next iteration
21+
if (collidingUrls.includes(project.repoUrl)) continue;
22+
const collisions = await models.Project.scan({
23+
repoUrl: project.repoUrl,
24+
archived: 'false'
25+
}).exec()
26+
// If scan found only this project go to a next interation
27+
if (collisions.length < 2) continue;
28+
logger.info(`Repository ${project.repoUrl} has ${collisions.length} collisions`);
29+
_.forEach(collisions, collision => {
30+
logger.info(`--- ID: ${collision.id}`)
31+
})
32+
collidingUrls.push(project.repoUrl)
33+
}
34+
previousKey = projects.lastKey
35+
previousSize = projects.scannedCount
36+
batch++
37+
}
38+
}
39+
main().then(() => {
40+
logger.info('Collision scan completed')
41+
}).catch(err => {
42+
logger.logFullError(err, 'collision scan')
43+
})

src/assets/WorkingWithTickets.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The basic flow for handling a ticket is as follows:
44

55
1. Assign the ticket to yourself, and the system will change the label to "tcx_Assigned", removing the "tcx_OpenForPickup" label. Please only assign tickets to yourself when you are ready to work on it. I don't want tickets assigned to someone and then not have them work on a ticket for 24 hours. The goal here is a quick turnaround for the client. If you can't work on a ticket immediately, leave it for someone else.
66

7-
1. Complete the ticket and create a merge request within 24 hours. Please ensure your merge request can be merged automatically (resolving any conflicts) and that it's against the latest commit in Git when you create it.
7+
1. Complete the ticket and create a merge request within 24 hours. Please ensure your merge request can be merged automatically (resolving any conflicts) and that it's against the latest commit in Git when you create it.
88

99
1. Change the label on the ticket to "tcx_ReadyForReview"
1010

@@ -24,7 +24,7 @@ If a fix is rejected, a comment, and possibly a screenshot, will be added to the
2424

2525
# Payment amounts
2626

27-
Each ticket in GitLab has a dollar value. That is the amount you will be paid when the ticket is completed, merged, and verified by the copilot. Note that there is still a 30 day waiting period as the payment will be treated as a regular TopCoder challenge payment.
27+
Each ticket in Gitlab / Github has a dollar value. That is the amount you will be paid when the ticket is completed, merged, and verified by the copilot. Note that there is still a 30 day waiting period as the payment will be treated as a regular TopCoder challenge payment.
2828

2929
# Important Rules:
3030

@@ -40,6 +40,6 @@ Each ticket in GitLab has a dollar value. That is the amount you will be paid w
4040

4141
- If an assigned task is not done in 24 hours, you will need to explain why it is not completed as a comment on the ticket.
4242

43-
- You can ask questions directly on the GitLab ticket.
43+
- You can ask questions directly on the Gitlab / Github ticket.
4444

45-
### ANYONE NOT FOLLOWING THE RULES ABOVE WILL BE WARNED AND POTENTIALLY LOSE THEIR GITLAB ACCESS!
45+
### ANYONE NOT FOLLOWING THE RULES ABOVE WILL BE WARNED AND POTENTIALLY LOSE THEIR GIT ACCESS!

src/config.js

+13-16
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ module.exports = {
2222

2323
// used as base to construct various URLs
2424
WEBSITE: process.env.WEBSITE || 'http://topcoderx.topcoder-dev.com',
25-
WEBSITE_SECURE: process.env.WEBSITE_SECURE || 'https://topcoderx.topcoder-dev.com',
2625
GITLAB_API_BASE_URL: process.env.GITLAB_API_BASE_URL || 'https://gitlab.com',
2726

2827
// kafka configuration
@@ -50,7 +49,6 @@ module.exports = {
5049
OPEN_FOR_PICKUP_ISSUE_LABEL: process.env.OPEN_FOR_PICKUP_ISSUE_LABEL || 'tcx_OpenForPickup',
5150
ALLOWED_TOPCODER_ROLES: process.env.ALLOWED_TOPCODER_ROLES || ['administrator', 'admin', 'connect manager', 'connect admin', 'copilot', 'connect copilot'],
5251
COPILOT_ROLE: process.env.COPILOT_ROLE || 'copilot',
53-
HELP_LINK: process.env.HELP_LINK || 'https://github.com/topcoder-platform/topcoder-x-ui/wiki',
5452
ADMINISTRATOR_ROLES: process.env.ADMINISTRATOR_ROLES || ['administrator', 'admin'],
5553
DYNAMODB: {
5654
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
@@ -149,20 +147,19 @@ const frontendConfigs = {
149147

150148
const activeEnv = module.exports.TOPCODER_ENV;
151149
module.exports.frontendConfigs = {
152-
helpLink: module.exports.HELP_LINK,
153150
copilotRole: module.exports.COPILOT_ROLE,
154151
administratorRoles: module.exports.ADMINISTRATOR_ROLES,
155-
JWT_V3_NAME: process.env.JWT_V3_NAME || frontendConfigs[activeEnv].JWT_V3_NAME,
156-
JWT_V2_NAME: process.env.JWT_V2_NAME || frontendConfigs[activeEnv].JWT_V2_NAME,
157-
COOKIES_SECURE: process.env.COOKIES_SECURE || frontendConfigs[activeEnv].COOKIES_SECURE,
158-
TC_LOGIN_URL: process.env.TC_LOGIN_URL || frontendConfigs[activeEnv].TC_LOGIN_URL,
159-
API_URL: process.env.API_URL || frontendConfigs[activeEnv].API_URL,
160-
ADMIN_TOOL_URL: process.env.ADMIN_TOOL_URL || frontendConfigs[activeEnv].ADMIN_TOOL_URL,
161-
ACCOUNTS_CONNECTOR_URL: process.env.ACCOUNTS_CONNECTOR_URL || frontendConfigs[activeEnv].ACCOUNTS_CONNECTOR_URL,
162-
CONNECT_URL_BASE: process.env.CONNECT_URL_BASE || frontendConfigs[activeEnv].CONNECT_URL_BASE,
163-
OWNER_LOGIN_GITHUB_URL: process.env.OWNER_LOGIN_GITHUB_URL || frontendConfigs[activeEnv].OWNER_LOGIN_GITHUB_URL,
164-
OWNER_LOGIN_GITLAB_URL: process.env.OWNER_LOGIN_GITLAB_URL || frontendConfigs[activeEnv].OWNER_LOGIN_GITLAB_URL,
165-
TOPCODER_URL: process.env.TOPCODER_URL || frontendConfigs[activeEnv].TOPCODER_URL,
166-
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || frontendConfigs[activeEnv].GITHUB_TEAM_URL,
167-
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || frontendConfigs[activeEnv].GITLAB_GROUP_URL,
152+
helpLink: process.env.HELP_LINK || 'https://github.com/topcoder-platform/topcoder-x-ui/wiki',
153+
JWT_V3_NAME: process.env.JWT_V3_NAME || 'v3jwt',
154+
JWT_V2_NAME: process.env.JWT_V2_NAME || 'tcjwt',
155+
COOKIES_SECURE: process.env.COOKIES_SECURE || false,
156+
TC_LOGIN_URL: process.env.TC_LOGIN_URL || 'https://accounts-auth0.topcoder-dev.com/',
157+
ADMIN_TOOL_URL: process.env.ADMIN_TOOL_URL || 'https://api.topcoder-dev.com/v2',
158+
ACCOUNTS_CONNECTOR_URL: process.env.ACCOUNTS_CONNECTOR_URL || 'https://accounts.topcoder-dev.com/connector.html',
159+
CONNECT_URL_BASE: process.env.CONNECT_URL_BASE || 'https://connect.topcoder-dev.com/projects/',
160+
OWNER_LOGIN_GITHUB_URL: process.env.OWNER_LOGIN_GITHUB_URL || '/api/v1/github/owneruser/login',
161+
OWNER_LOGIN_GITLAB_URL: process.env.OWNER_LOGIN_GITLAB_URL || '/api/v1/gitlab/owneruser/login',
162+
TOPCODER_URL: process.env.TOPCODER_URL || 'https://topcoder-dev.com',
163+
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || 'https://github.com/orgs/',
164+
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || 'https://gitlab.com/groups/',
168165
};

src/controllers/GithubController.js

+4
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ async function addUserToTeamCallback(req, res) {
151151
.query({client_id: config.GITHUB_CLIENT_ID, client_secret: config.GITHUB_CLIENT_SECRET, code})
152152
.set('Accept', 'application/json')
153153
.end();
154+
// Throw error if github access token was not returned (e.g. invalid code)
155+
if (!result.body.access_token) {
156+
throw new errors.UnauthorizedError('Github authorization failed.', result.body.error_description);
157+
}
154158
const token = result.body.access_token;
155159
// add user to team
156160
console.log(`adding ${token} to ${team.teamId} with ${team.ownerToken}`); /* eslint-disable-line no-console */

src/controllers/GitlabController.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ async function addUserToGroupCallback(req, res) {
188188
redirect_uri: `${config.WEBSITE}/api/${config.API_VERSION}/gitlab/normaluser/callback`,
189189
})
190190
.end();
191+
// Throw error if github access token was not returned (ex. invalid code)
192+
if (!result.body.access_token) {
193+
throw new errors.UnauthorizedError('Gitlab authorization failed.', result.body.error_description);
194+
}
191195
const token = result.body.access_token;
192196

193197
// get group name
@@ -235,7 +239,8 @@ async function addUserToGroupCallback(req, res) {
235239
});
236240
}
237241
// redirect to success page
238-
res.redirect(`${constants.USER_ADDED_TO_TEAM_SUCCESS_URL}/gitlab/${currentGroup.full_path}`);
242+
// For gitlab subgroups we need to replace / with something different. Default encoding doesn't work as angular route fails to match %2F
243+
res.redirect(`${constants.USER_ADDED_TO_TEAM_SUCCESS_URL}/gitlab/${currentGroup.full_path.replace('/', '@!2F')}`);
239244
}
240245

241246

src/front/src/app/less/custom.less

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
color: green;
2121
font-size: 16px;
2222
}
23-
23+
.red-times-icon {
24+
color: red;
25+
font-size: 16px;
26+
}
2427
.orange-warning-icon {
2528
color: orange;
2629
font-size: 16px;

src/front/src/app/members/member.controller.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ angular.module('topcoderX')
1111
const org = params[0];
1212
const team = url.replace(org, '').substring(1);
1313
$scope.link = $rootScope.appConfig.GITHUB_TEAM_URL + org + '/teams/' + team;
14-
} else if (provider === 'github') {
15-
$scope.link = $rootScope.appConfig.GITLAB_GROUP_URL + url;
14+
} else if (provider === 'gitlab') {
15+
// For gitlab subgroups we can't just pass encoded link to this route because anguler doesn't match %2F, so we use @!2F as a workaround
16+
$scope.link = $rootScope.appConfig.GITLAB_GROUP_URL + url.replace('@!2F', '/');
1617
}
1718
};
1819
_getUrl($scope.provider, $stateParams.url);

src/front/src/app/projects/projects.html

+9
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ <h4>You don't have active projects right now. Please
6161
<th class="col-lg-2">Service Provider</th>
6262
<th class="col-lg-2" ng-show="isAdminUser">Owner</th>
6363
<th class="col-lg-2">Copilot</th>
64+
<th class="col-lg-2">Copilot payments</th>
6465
<th class="col-lg-3" data-sort-ignore="true"></th>
6566
</tr>
6667
</thead>
@@ -78,6 +79,14 @@ <h4>You don't have active projects right now. Please
7879
</td>
7980
<td class="col-lg-2" ng-show="isAdminUser">{{project.owner}}</td>
8081
<td class="col-lg-2">{{project.copilot}}</td>
82+
<td class="col-lg-2" data-value="{{project.createCopilotPayments}}">
83+
<div ng-if="project.createCopilotPayments === 'true'">
84+
<i class="fa fa-check green-check-icon"/>
85+
</div>
86+
<div ng-if="project.createCopilotPayments === 'false'">
87+
<i class="fa fa-times red-times-icon"/>
88+
</div>
89+
</td>
8190
<td class="col-lg-2">
8291
<button class="btn btn-sm btn-success" ng-click="goProject(project)">
8392
<strong>Manage</strong>

src/services/GithubService.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ getTeamRegistrationUrl.schema = Joi.object().keys({
178178
token: Joi.string().required(),
179179
ownerUsername: Joi.string().required(),
180180
teamId: Joi.string().required(),
181-
accessLevel: Joi.string().required(),
181+
accessLevel: Joi.string().valid('member', 'maintainer').required(),
182182
});
183183

184184
/**

src/services/ProjectService.js

+14
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ const createProjectSchema = {
7272
* @private
7373
*/
7474
async function _validateProjectData(project) {
75+
const filter = {
76+
repoUrl: project.repoUrl,
77+
archived: 'false'
78+
}
79+
if (project.id) {
80+
filter.id = {
81+
ne: project.id
82+
}
83+
}
84+
const existsInDatabase = await dbHelper.scanOne(models.Project, filter)
85+
if (existsInDatabase) {
86+
throw new errors.ValidationError(`This repo already has a Topcoder-X project associated with it.
87+
Copilot: ${existsInDatabase.copilot}, Owner: ${existsInDatabase.owner}`)
88+
}
7589
const provider = await helper.getProviderType(project.repoUrl);
7690
const userRole = project.copilot ? project.copilot : project.owner;
7791
const setting = await userService.getUserSetting(userRole);

0 commit comments

Comments
 (0)