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

Commit 96988f9

Browse files
committed
Merge remote-tracking branch 'github/develop'
2 parents fbe5a47 + 33fa57b commit 96988f9

33 files changed

+1308
-35
lines changed

TCXSequenceDiagrams.tcuml

53.4 KB
Binary file not shown.

configuration.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ The following config parameters are supported, they are defined in `src/config.j
2424
| HOOK_BASE_URL | The base URL of the topcoder-x-receiver, used when adding webhooks automatically to repositories | |
2525
| TOPCODER_ENV | The topcoder environment to use, can support 'dev' or 'prod' | 'dev' |
2626
|LABELS| Labels we are going to add to the repository in the form of array of object with `name` and `color` property. Color should be hex code without hash||
27+
|ALLOWED_TOPCODER_ROLES| The allowed Topcoder role to use Topcoder X app| see configuration |
28+
|COPILOT_ROLE| The role to identify copilot|'copilot'|
29+
|HELP_LINK| The link for help| 'https://github.com/topcoder-platform/topcoder-x-ui/wiki'|
2730

2831
## GitHub OAuth App Setup
2932

src/app.js

+14
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ _.forEach(routes, (verbs, path) => {
7676
return next();
7777
});
7878
}
79+
if (def.allowedRoles) {
80+
actions.push((req, res, next) => {
81+
// check if user has allowed roles
82+
if (_(req.currentUser.roles).map((i) => i.toLowerCase())
83+
.intersection(_.map(def.allowedRoles, (j) => j.toLowerCase())).size() === 0) {
84+
const statusCode = 403;
85+
return res.status(statusCode).json({
86+
code: 'Forbidden',
87+
message: 'You are not allowed to access this resource.',
88+
});
89+
}
90+
return next();
91+
});
92+
}
7993
actions.push(method);
8094
app[verb](`/api/${config.API_VERSION}${path}`, actions);
8195
});

src/assets/WorkingWithTickets.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# How to work with git tickets
2+
3+
The basic flow for handling a ticket is as follows:
4+
5+
1. Assign the ticket to yourself, change the label to "tcx_Assigned", remove 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.
6+
7+
1. Complete the ticket and create a merge request within 24 hours. Please ensure your merge request can be merged automatically and that it's against the latest commit in Git when you create it.
8+
9+
1. Change the label on the ticket to "tcx_ReadyForReview"
10+
11+
After seeing a ticket marked as "tcx_ReadyForReview", the copilot will review that ticket, usually within 24 hours.
12+
13+
Note that you are expected to keep your changes in-sync with Git - make sure to do a pull before you push changes to make sure there aren't any merge issues.
14+
15+
### Accepted fix
16+
17+
If a fix is accepted, a payment ticket will be created on the Topcoder platform within 5-10 minutes of the issue being closed. You should see the payment in your PACTs within 24 hours.
18+
19+
### Rejected fix
20+
21+
If a fix is rejected, a comment, and possibly a screenshot, will be added to the ticket explaining why the fix was rejected. The status will be changed to "tcx_Feedback".
22+
23+
**If a fix is rejected, that ticket is your priority. You should not assign yourself any more tickets until you complete the required additional fixes!**
24+
25+
# Payment amounts
26+
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.
28+
29+
# Important Rules:
30+
31+
- You can assign any unassigned issue to yourself with an "Open for pick up" label (first come first serve)
32+
33+
- You can only assign **ONE AT A TIME**. The nature of it being assigned will indicate it is not available to anyone else.
34+
35+
- You will fix the ticket by committing changes to the master branch.
36+
37+
- After marking a ticket "tcx_ReadyForReview" you are eligible to accept another. You do _NOT_ need to wait for the copilot to validate your fix.
38+
39+
- You can do as many tickets as you want, as long as you follow the rules above.
40+
41+
- 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.
42+
43+
- You can ask questions directly on the GitLab ticket.
44+
45+
### ANYONE NOT FOLLOWING THE RULES ABOVE WILL BE WARNED AND POTENTIALLY LOSE THEIR GITLAB ACCESS!

src/config.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ module.exports = {
1515
MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/topcoderx',
1616
SESSION_SECRET: process.env.SESSION_SECRET || 'kjsdfkj34857',
1717
// Github and gitlab client id and secret
18-
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID || '',
19-
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET || '',
20-
GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID || '',
21-
GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET || '',
18+
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID || 'ae39bea2a2a23f1dd032',
19+
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET || 'f31dd2da12015f60372a5312a40a06b517a88702',
20+
GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID || 'af4c1ea3d12783e55470a6604c169b2cdc03734c7e3969f7f4336325f6e0e6b4',
21+
GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET || '8906a8e924df81ddadd12b37ee98767cfba3084ca3c9d52a5753412da8d9879e',
2222

2323
// used as base to construct various URLs
2424
WEBSITE: process.env.WEBSITE || 'http://topcoderx.topcoder-dev.com',
@@ -30,10 +30,13 @@ module.exports = {
3030
ssl: {
3131
cert: process.env.KAFKA_CLIENT_CERT || fs.readFileSync('./kafka_client.cer'), // eslint-disable-line no-sync
3232
key: process.env.KAFKA_CLIENT_CERT_KEY || fs.readFileSync('./kafka_client.key'), // eslint-disable-line no-sync
33+
passphrase: 'secret', // NOTE:* This configuration specifies the private key passphrase used while creating it.
3334
},
3435
},
35-
HOOK_BASE_URL: process.env.HOOK_BASE_URL || 'http://x.topcoder-dev.com',
36+
HOOK_BASE_URL: process.env.HOOK_BASE_URL || 'http://topcoderx.topcoder-dev.com',
3637
TOPCODER_ENV: process.env.TOPCODER_ENV || 'dev',
37-
LABELS: process.env.LABELS || [{ name: 'Open for pickup', color: '428BCA' }, { name: 'Assigned', color: '004E00' }, { name: 'Ready for review', color: 'D1D100' }, { name: 'Paid', color: '7F8C8D' }, { name: 'Feedback', color: 'FF0000' }, { name: 'Fix accepted', color: '69D100' }],
38+
LABELS: process.env.LABELS || [{ name: 'tcx_OpenForPickup', color: '428BCA' }, { name: 'tcx_Assigned', color: '004E00' }, { name: 'tcx_ReadyForReview', color: 'D1D100' }, { name: 'tcx_Paid', color: '7F8C8D' }, { name: 'tcx_Feedback', color: 'FF0000' }, { name: 'tcx_FixAccepted', color: '69D100' }],
3839
ALLOWED_TOPCODER_ROLES: process.env.ALLOWED_TOPCODER_ROLES || ['administrator', 'admin', 'connect manager', 'connect admin', 'copilot', 'connect copilot'],
40+
COPILOT_ROLE: process.env.COPILOT_ROLE || 'copilot',
41+
HELP_LINK: process.env.HELP_LINK || 'https://github.com/topcoder-platform/topcoder-x-ui/wiki',
3942
};
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2018 TopCoder, Inc. All rights reserved.
3+
*/
4+
5+
/**
6+
* This controller exposes application configuration related endpoints.
7+
*
8+
* @author veshu
9+
* @version 1.0
10+
*/
11+
const helper = require('../common/helper');
12+
const AppConfigService = require('../services/AppConfigService');
13+
14+
/**
15+
* gets the application configuration required for frontend
16+
* @returns {Object} the configuration details
17+
*/
18+
async function getAppConfig() {
19+
return await AppConfigService.getAppConfig();
20+
}
21+
22+
module.exports = {
23+
getAppConfig,
24+
};
25+
26+
helper.buildController(module.exports);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2018 TopCoder, Inc. All rights reserved.
3+
*/
4+
5+
/**
6+
* This controller exposes payment endpoints.
7+
*
8+
* @author kevinkid
9+
* @version 1.0
10+
*/
11+
const helper = require('../common/helper');
12+
const CopilotPaymentService = require('../services/CopilotPaymentService');
13+
14+
/**
15+
* update payments status
16+
* @param {Object} req the request
17+
* @param {Object} res the response
18+
* @returns {Promise} fetch payment updates execution
19+
*/
20+
async function updateAll(req) {
21+
return await CopilotPaymentService.updateAll(req.currentUser);
22+
}
23+
24+
/**
25+
* get all the payment for the current copilot
26+
* @param {Object} req the request
27+
* @param {Object} res the response
28+
* @returns {Object} the result
29+
*/
30+
async function getAll(req) {
31+
const payments = await CopilotPaymentService.getAll(req.query, req.currentUser);
32+
const active = [];
33+
const closed = [];
34+
35+
payments.forEach(function (payment) {
36+
if (payment.closed === true) {
37+
closed.push(payment);
38+
} else {
39+
active.push(payment);
40+
}
41+
});
42+
return { activePayments: active, closedPayments: closed };
43+
}
44+
45+
/**
46+
* create payment
47+
* @param {Object} req the request
48+
* @param {Object} res the response
49+
* @returns {Object} the result
50+
*/
51+
async function create(req) {
52+
return await CopilotPaymentService.create(req.currentUser, req.body.payment);
53+
}
54+
55+
/**
56+
* update payment item
57+
* @param {Object} req the request
58+
* @param {Object} res the response
59+
* @returns {Object} the result
60+
*/
61+
async function update(req) {
62+
return await CopilotPaymentService.update(req.currentUser, req.body.payment);
63+
}
64+
65+
/**
66+
* remove payment item
67+
* @param {Object} req the request
68+
* @param {Object} res the response
69+
* @returns {Object} the result
70+
*/
71+
async function remove(req) {
72+
return await CopilotPaymentService.remove(req.params.id, req.currentUser);
73+
}
74+
75+
76+
module.exports = {
77+
getAll,
78+
create,
79+
update,
80+
remove,
81+
updateAll
82+
};
83+
84+
helper.buildController(module.exports);

src/controllers/ProjectController.js

+11
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,23 @@ async function createHook(req) {
6060
return await ProjectService.createHook(req.body, req.currentUser.handle);
6161
}
6262

63+
/**
64+
* adds the wiki rules the project's repository
65+
* @param {Object} req the request
66+
* @param {Object} res the response
67+
* @returns {Object} the result
68+
*/
69+
async function addWikiRules(req) {
70+
return await ProjectService.addWikiRules(req.body, req.currentUser.handle);
71+
}
72+
6373
module.exports = {
6474
create,
6575
update,
6676
getAll,
6777
createLabel,
6878
createHook,
79+
addWikiRules,
6980
};
7081

7182
helper.buildController(module.exports);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
3+
angular.module('topcoderX')
4+
.controller('AddCopilotPaymentController', ['$scope', '$log', '$state', 'CopilotPaymentService', 'ProjectService', '$filter', '$rootScope', '$timeout', 'Alert',
5+
function ($scope, $log, $state, CopilotPaymentService, ProjectService, $filter, $rootScope, $timeout, Alert) {
6+
// below logic is trying to identify whether we are editing a payment
7+
$scope.editing = true;
8+
$scope.projects = [];
9+
$scope.payment = {
10+
project: null,
11+
amount: null,
12+
description: '',
13+
};
14+
if ($rootScope.payment) {
15+
$scope.title = 'Edit a Payment';
16+
$scope.payment = angular.copy($rootScope.payment);
17+
$scope.payment.id = $rootScope.payment.id;
18+
$scope.payment.project = $rootScope.payment.project.id;
19+
$scope.editing = true;
20+
} else {
21+
$scope.title = 'Add a Payment';
22+
$scope.editing = false;
23+
}
24+
25+
// get topcoderx projects
26+
$scope.getProjects = function () {
27+
ProjectService.getProjects().then(function (response) {
28+
$scope.projects = response.data;
29+
}).catch(function (error) {
30+
_handleError(error, 'There are no projects in Topcoder-X. Please create a project first.');
31+
});
32+
};
33+
34+
$scope.getProjects();
35+
36+
// handle error output
37+
function _handleError(error, defaultMsg) {
38+
const errMsg = error.data ? error.data.message : defaultMsg;
39+
Alert.error(errMsg, $scope);
40+
}
41+
42+
// create/update payment item
43+
$scope.save = function () {
44+
if (!$scope.editing) {
45+
CopilotPaymentService.create($scope.payment).then(function () {
46+
$state.go('app.copilotPayments');
47+
}).catch(function (error) {
48+
_handleError(error, 'An error occurred while creating Payment.');
49+
});
50+
}
51+
if ($scope.editing) {
52+
CopilotPaymentService.update({
53+
id: $scope.payment.id,
54+
project: $scope.payment.project,
55+
amount: $scope.payment.amount,
56+
description: $scope.payment.description,
57+
}).then(function () {
58+
$rootScope.payment = null;
59+
$state.go('app.copilotPayments');
60+
}).catch(function (error) {
61+
_handleError(error, 'An error occurred while updating Payment.');
62+
});
63+
}
64+
};
65+
}
66+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<div ng-controller="AddCopilotPaymentController">
2+
<div class="col-md-10 col-md-offset-1" ng-include src="'components/alert/alert.html'"></div>
3+
<div class="row wrapper border-bottom white-bg page-heading">
4+
<div class="col-lg-4">
5+
<h2>{{title}}</h2>
6+
</div>
7+
</div>
8+
9+
<div class="wrapper wrapper-content animated fadeInRight">
10+
<div class="row">
11+
<div class="col-lg-12">
12+
<div class="text-center m-t-lg">
13+
<form name="paymentForm">
14+
<label class="form-label">Project:</label>
15+
<select ng-model="payment.project" class="form-control" required>
16+
<option ng-repeat="project in projects" value={{project.id}}>{{project.title}}</option>
17+
</select>
18+
<span ng-show="paymentForm.payment.project.$touched && paymentForm.payment.project.$invalid">The payment Project is required.</span>
19+
<br />
20+
<label class="form-label">Amount:</label>
21+
<input class="form-control" type="number" ng-model="payment.amount" required/>
22+
<span ng-show="paymentForm.payment.amount.$touched && paymentForm.payment.amount.$invalid">The payment Amount is required.</span>
23+
<br />
24+
<label class="form-label">Description:</label>
25+
<input class="form-control" type="text" ng-model="payment.description" required/>
26+
<span ng-show="paymentForm.payment.description.$touched && paymentForm.payment.description.$invalid">The payment Description is required.</span>
27+
<br />
28+
<br />
29+
<button type="submit" class="with-button btn btn-sm btn-info" ng-click="paymentForm.$valid && save()">
30+
<strong>
31+
Save
32+
</strong>
33+
</button>
34+
</form>
35+
</div>
36+
</div>
37+
</div>
38+
</div>
39+
</div>
40+
41+
42+
43+

src/front/src/app/app.js

+15
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,23 @@ angular.module('topcoderX', [
128128
templateUrl: 'app/members/member.html',
129129
controller: 'MemberController',
130130
controllerAs: 'vm',
131+
})
132+
.state('app.copilotPayments', {
133+
url: '/copilot-payments',
134+
templateUrl: 'app/copilot-payments/copilot-payments.html',
135+
controller: 'CopilotPaymentsController',
136+
controllerAs: 'vm',
137+
resolve: { auth: authenticate }
138+
})
139+
.state('app.addPayment', {
140+
url: '/copilot-payment',
141+
templateUrl: 'app/add-copilot-payment/add-copilot-payment.html',
142+
controller: 'AddCopilotPaymentController',
143+
controllerAs: 'vm',
144+
resolve: { auth: authenticate }
131145
});
132146

147+
133148
$urlRouterProvider.otherwise('/app/main');
134149
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/);
135150
}]);

0 commit comments

Comments
 (0)