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

Commit b40a5b0

Browse files
committed
Use old token. Change prod challenge api to v3. Clean lint.
1 parent d6d6e41 commit b40a5b0

File tree

8 files changed

+106
-36
lines changed

8 files changed

+106
-36
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ The following config parameters are supported, they are defined in `config/defau
3030
| PARTITION | the kafka partition | 0|
3131
| KAFKA_OPTIONS | the connection option for kafka | see below about KAFKA options |
3232
|TC_DEV_ENV| the flag whether to use topcoder development api or production| false|
33+
| TC_AUTHN_URL | the Topcoder authentication url | https://topcoder-dev.auth0.com/oauth/ro |
34+
| TC_AUTHN_REQUEST_BODY | the Topcoder authentication request body. This makes use of some environment variables: `TC_USERNAME`, `TC_PASSWORD`, `TC_CLIENT_ID`, `CLIENT_V2CONNECTION` | see `default.js` |
35+
| TC_AUTHZ_URL | the Topcoder authorization url | https://api.topcoder-dev.com/v3/authorizations |
3336
| NEW_CHALLENGE_TEMPLATE | the body template for new challenge request. You can change the subTrack, reviewTypes, technologies, .. here | see `default.js` |
3437
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
3538
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|

config/default.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ module.exports = {
2626
}
2727
},
2828
TC_DEV_ENV: process.env.NODE_ENV === 'production' ? false : true,
29+
TC_AUTHN_URL: process.env.TC_AUTHN_URL || 'https://topcoder-dev.auth0.com/oauth/ro',
30+
TC_AUTHN_REQUEST_BODY: {
31+
username: process.env.TC_USERNAME || 'mess',
32+
password: process.env.TC_PASSWORD || 'appirio123',
33+
client_id: process.env.TC_CLIENT_ID || 'JFDo7HMkf0q2CkVFHojy3zHWafziprhT',
34+
sso: false,
35+
scope: 'openid profile offline_access',
36+
response_type: 'token',
37+
connection: process.env.CLIENT_V2CONNECTION || 'TC-User-Database',
38+
grant_type: 'password',
39+
device: 'Browser'
40+
},
41+
TC_AUTHZ_URL: process.env.TC_AUTHZ_URL || 'https://api.topcoder-dev.com/v3/authorizations',
2942
NEW_CHALLENGE_TEMPLATE: process.env.NEW_CHALLENGE_TEMPLATE || {
3043
milestoneId: 1,
3144
subTrack: 'FIRST_2_FINISH',

configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ The following config parameters are supported, they are defined in `config/defau
1313
|KAFKA_CLIENT_CERT | The Kafka SSL certificate to use when connecting| Read from kafka_client.cer file, but this can be set as a string like it is on Heroku |
1414
|KAFKA_CLIENT_CERT_KEY | The Kafka SSL certificate key to use when connecting| Read from kafka_client.key file, but this can be set as a string like it is on Heroku|
1515
|TC_DEV_ENV| the flag whether to use topcoder development api or production| false|
16+
| TC_AUTHN_URL | the Topcoder authentication url | https://topcoder-dev.auth0.com/oauth/ro |
17+
| TC_AUTHN_REQUEST_BODY | the Topcoder authentication request body. This makes use of some environment variables: `TC_USERNAME`, `TC_PASSWORD`, `TC_CLIENT_ID`, `CLIENT_V2CONNECTION` | see `default.js` |
18+
| TC_AUTHZ_URL | the Topcoder authorization url | https://api.topcoder-dev.com/v3/authorizations |
1619
| NEW_CHALLENGE_TEMPLATE | the body template for new challenge request. You can change the subTrack, reviewTypes, technologies, .. here | see `default.js` |
1720
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
1821
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
},
2525
"homepage": "https://gitlab.com/luettich/processor#README",
2626
"dependencies": {
27-
"@topcoder-platform/topcoder-api-challenges-v4-wrapper": "^1.0.5",
2827
"@topcoder-platform/topcoder-api-challenges-v4-wrapper-dev": "^1.0.5",
2928
"axios": "^0.19.0",
3029
"circular-json": "^0.5.7",
@@ -43,6 +42,7 @@
4342
"node-gitlab-api": "^2.2.8",
4443
"nodemailer": "^4.6.7",
4544
"tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.6.3",
45+
"topcoder-api-challenges": "^1.0.6",
4646
"topcoder-api-projects": "^1.0.1",
4747
"topcoder-dev-api-projects": "^1.0.1",
4848
"topcoder-healthcheck-dropin": "^1.0.3",

services/IssueService.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,13 @@ async function handleIssueAssignment(event, issue, force = false) {
197197
dbIssue = await ensureChallengeExists(event, issue);
198198

199199
if (!dbIssue) {
200-
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
200+
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
201201
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
202202
// Handle it for rescheduling.
203203
await eventService.handleEventGracefully(event, issue, err);
204204
return;
205205
}
206-
206+
207207
// Handle multiple assignees. TC-X allows only one assignee.
208208
if (event.data.issue.assignees && event.data.issue.assignees.length > 1) {
209209
const comment = 'Topcoder-X only supports a single assignee on a ticket to avoid issues with payment';
@@ -313,7 +313,7 @@ async function handleIssueUpdate(event, issue) {
313313
dbIssue = await ensureChallengeExists(event, issue, false);
314314

315315
if (!dbIssue) {
316-
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
316+
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
317317
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
318318
// Handle it for rescheduling.
319319
await eventService.handleEventGracefully(event, issue, err);
@@ -363,9 +363,9 @@ async function handleIssueClose(event, issue) {
363363
let dbIssue;
364364
try {
365365
dbIssue = await ensureChallengeExists(event, issue);
366-
366+
367367
if (!dbIssue) {
368-
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
368+
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
369369
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
370370
// Handle it for rescheduling.
371371
await eventService.handleEventGracefully(event, issue, err);
@@ -376,11 +376,11 @@ async function handleIssueClose(event, issue) {
376376

377377
// if the issue has payment success or payment pending status, we'll ignore this process.
378378
if (dbIssue && dbIssue.status === 'challenge_payment_successful') {
379-
logger.debug(`Ignoring close issue processing. The issue has challenge_payment_successful.`);
379+
logger.debug('Ignoring close issue processing. The issue has challenge_payment_successful.');
380380
return;
381381
}
382382
if (dbIssue && dbIssue.status === 'challenge_payment_pending') {
383-
logger.debug(`Ignoring close issue processing. The issue has challenge_payment_pending.`);
383+
logger.debug('Ignoring close issue processing. The issue has challenge_payment_pending.');
384384
return;
385385
}
386386

@@ -641,7 +641,7 @@ async function handleIssueLabelUpdated(event, issue) {
641641
// Sometimes Github send label updated event before issue created event.
642642
// This process will be ignored. The label will be processed (stored) at hanleIssueCreated.
643643
if (!dbIssue) {
644-
logger.debug(`DB record not found. Issue label update ignored.`);
644+
logger.debug('DB record not found. Issue label update ignored.');
645645
return;
646646
}
647647
await dbHelper.update(models.Issue, dbIssue.id, {
@@ -662,7 +662,7 @@ async function handleIssueUnAssignment(event, issue) {
662662
dbIssue = await ensureChallengeExists(event, issue);
663663

664664
if (!dbIssue) {
665-
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
665+
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
666666
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
667667
// Handle it for rescheduling.
668668
await eventService.handleEventGracefully(event, issue, err);

utils/db-helper.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async function updateMany(model, collection) {
8181
return reject(err);
8282
}
8383

84-
resolve(result);
84+
return resolve(result);
8585
});
8686
});
8787
}
@@ -142,7 +142,7 @@ async function remove(Model, queryParams) {
142142
return reject(err);
143143
}
144144

145-
resolve(dbItem);
145+
return resolve(dbItem);
146146
});
147147
}
148148
});

utils/logger.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
* @version 1.0
1010
*/
1111
'use strict';
12-
const config = require('config');
1312
const util = require('util');
13+
const config = require('config');
1414
const _ = require('lodash');
1515
const winston = require('winston');
1616
const getParams = require('get-parameter-names');

utils/topcoder-api-helper.js

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@
1313
'use strict';
1414

1515
const config = require('config');
16+
const jwtDecode = require('jwt-decode');
1617
const axios = require('axios');
1718
const _ = require('lodash');
1819
const moment = require('moment');
1920
const circularJSON = require('circular-json');
2021

21-
const m2mAuth = require('tc-core-library-js').auth.m2m;
22+
// const m2mAuth = require('tc-core-library-js').auth.m2m;
2223

23-
const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL']));
24+
// const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL']));
2425

2526
let topcoderApiProjects = require('topcoder-api-projects');
26-
let topcoderApiChallenges = require('@topcoder-platform/topcoder-api-challenges-v4-wrapper');
27+
let topcoderApiChallenges = require('topcoder-api-challenges');
2728

2829
const topcoderDevApiProjects = require('topcoder-dev-api-projects');
2930
const topcoderDevApiChallenges = require('@topcoder-platform/topcoder-api-challenges-v4-wrapper-dev');
@@ -37,12 +38,16 @@ if (config.TC_DEV_ENV) {
3738
topcoderApiProjects = topcoderDevApiProjects;
3839
topcoderApiChallenges = topcoderDevApiChallenges;
3940
}
41+
42+
// Cache the access token
43+
let cachedAccessToken;
44+
4045
// Init the API instances
4146
const projectsClient = topcoderApiProjects.ApiClient.instance;
4247
const challengesClient = topcoderApiChallenges.ApiClient.instance;
4348

44-
//Timeout increase to 5 minutes
45-
challengesClient.timeout=300000;
49+
// Timeout increase to 5 minutes
50+
challengesClient.timeout = 300000;
4651

4752
const bearer = projectsClient.authentications.bearer;
4853
bearer.apiKeyPrefix = 'Bearer';
@@ -51,20 +56,66 @@ const projectsApiInstance = new topcoderApiProjects.DefaultApi();
5156
const challengesApiInstance = new topcoderApiChallenges.DefaultApi();
5257

5358
/**
54-
* Function to get M2M token
55-
* @returns {Promise} The promised token
59+
* Authenticate with Topcoder API and get the access token.
60+
* @returns {String} the access token issued by Topcoder
61+
* @private
5662
*/
57-
async function getM2Mtoken() {
58-
return await m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET);
63+
async function getAccessToken() {
64+
// Check the cached access token
65+
if (cachedAccessToken) {
66+
const decoded = jwtDecode(cachedAccessToken);
67+
if (decoded.iat > new Date().getTime()) {
68+
// Still not expired, just use it
69+
return cachedAccessToken;
70+
}
71+
}
72+
73+
// Authenticate
74+
const v2Response = await axios.post(config.TC_AUTHN_URL, config.TC_AUTHN_REQUEST_BODY);
75+
const v2IdToken = _.get(v2Response, 'data.id_token');
76+
const v2RefreshToken = _.get(v2Response, 'data.refresh_token');
77+
78+
if (!v2IdToken || !v2RefreshToken) {
79+
throw new Error(`cannot authenticate with topcoder: ${config.TC_AUTHN_URL}`);
80+
}
81+
82+
// Authorize
83+
const v3Response = await axios.post(
84+
config.TC_AUTHZ_URL, {
85+
param: {
86+
externalToken: v2IdToken,
87+
refreshToken: v2RefreshToken
88+
}
89+
}, {
90+
headers: {
91+
authorization: `Bearer ${v2IdToken}`
92+
}
93+
});
94+
95+
cachedAccessToken = _.get(v3Response, 'data.result.content.token');
96+
97+
if (!cachedAccessToken) {
98+
throw new Error(`cannot authorize with topcoder: ${config.TC_AUTHZ_URL}`);
99+
}
100+
101+
return cachedAccessToken;
59102
}
60103

104+
// /**
105+
// * Function to get M2M token
106+
// * @returns {Promise} The promised token
107+
// */
108+
// async function getM2Mtoken() {
109+
// return await m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET);
110+
// }
111+
61112
/**
62113
* Create a new project.
63114
* @param {String} projectName the project name
64115
* @returns {Number} the created project id
65116
*/
66117
async function createProject(projectName) {
67-
bearer.apiKey = await getM2Mtoken();
118+
bearer.apiKey = await getAccessToken();
68119
// eslint-disable-next-line new-cap
69120
const projectBody = new topcoderApiProjects.ProjectRequestBody.constructFromObject({
70121
projectName
@@ -96,7 +147,7 @@ async function createProject(projectName) {
96147
* @returns {Number} the created challenge id
97148
*/
98149
async function createChallenge(challenge) {
99-
bearer.apiKey = await getM2Mtoken();
150+
bearer.apiKey = await getAccessToken();
100151
const start = new Date();
101152
const startTime = moment(start).toISOString();
102153
const end = moment(start).add(config.NEW_CHALLENGE_DURATION_IN_DAYS, 'days').toISOString();
@@ -137,7 +188,7 @@ async function createChallenge(challenge) {
137188
* @param {Object} challenge the challenge to update
138189
*/
139190
async function updateChallenge(id, challenge) {
140-
bearer.apiKey = await getM2Mtoken();
191+
bearer.apiKey = await getAccessToken();
141192
logger.debug(`Updating challenge ${id} with ${circularJSON.stringify(challenge)}`);
142193
// eslint-disable-next-line new-cap
143194
const challengeBody = new topcoderApiChallenges.UpdateChallengeBodyParam.constructFromObject({
@@ -175,7 +226,7 @@ async function updateChallenge(id, challenge) {
175226
* @param {Number} id the challenge id
176227
*/
177228
async function activateChallenge(id) {
178-
bearer.apiKey = await getM2Mtoken();
229+
bearer.apiKey = await getAccessToken();
179230
logger.debug(`Activating challenge ${id}`);
180231
try {
181232
const response = await new Promise((resolve, reject) => {
@@ -215,7 +266,7 @@ async function getChallengeById(id) {
215266
if (!_.isNumber(id)) {
216267
throw new Error('The challenge id must valid number');
217268
}
218-
const apiKey = await getM2Mtoken();
269+
const apiKey = await getAccessToken();
219270
logger.debug('Getting topcoder challenge details');
220271
try {
221272
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}`, {
@@ -246,7 +297,7 @@ async function getChallengeById(id) {
246297
* @param {Number} winnerId the winner id
247298
*/
248299
async function closeChallenge(id, winnerId) {
249-
const apiKey = await getM2Mtoken();
300+
const apiKey = await getAccessToken();
250301
logger.debug(`Closing challenge ${id}`);
251302
try {
252303
const response = await axios.post(`${challengesClient.basePath}/challenges/${id}/close?winnerId=${winnerId}`, null, {
@@ -276,7 +327,7 @@ async function closeChallenge(id, winnerId) {
276327
* @returns {Number} the billing account id
277328
*/
278329
async function getProjectBillingAccountId(id) {
279-
const apiKey = await getM2Mtoken();
330+
const apiKey = await getAccessToken();
280331
logger.debug(`Getting project billing detail ${id}`);
281332
try {
282333
const response = await axios.get(`${projectsClient.basePath}/direct/projects/${id}`, {
@@ -308,7 +359,7 @@ async function getProjectBillingAccountId(id) {
308359
* @returns {Number} the user id
309360
*/
310361
async function getTopcoderMemberId(handle) {
311-
bearer.apiKey = await getM2Mtoken();
362+
bearer.apiKey = await getAccessToken();
312363
try {
313364
const response = await axios.get(`${projectsClient.basePath}/members/${handle}`);
314365
const statusCode = response ? response.status : null;
@@ -327,7 +378,7 @@ async function getTopcoderMemberId(handle) {
327378
* @param {Object} resource the resource resource to add
328379
*/
329380
async function addResourceToChallenge(id, resource) {
330-
bearer.apiKey = await getM2Mtoken();
381+
bearer.apiKey = await getAccessToken();
331382
logger.debug(`adding resource to challenge ${id}`);
332383
try {
333384
const response = await new Promise((resolve, reject) => {
@@ -368,7 +419,7 @@ async function getResourcesFromChallenge(id) {
368419
if (!_.isNumber(id)) {
369420
throw new Error('The challenge id must valid number');
370421
}
371-
const apiKey = await getM2Mtoken();
422+
const apiKey = await getAccessToken();
372423
logger.debug(`fetch resource from challenge ${id}`);
373424
try {
374425
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}/resources`, {
@@ -413,7 +464,7 @@ async function roleAlreadySet(id, role) {
413464
* @param {Object} resource the resource resource to remove
414465
*/
415466
async function unregisterUserFromChallenge(id) {
416-
bearer.apiKey = await getM2Mtoken();
467+
bearer.apiKey = await getAccessToken();
417468
logger.debug(`removing resource from challenge ${id}`);
418469
try {
419470
const response = await new Promise((resolve, reject) => {
@@ -450,7 +501,7 @@ async function unregisterUserFromChallenge(id) {
450501
* @param {Number} id the challenge id
451502
*/
452503
async function cancelPrivateContent(id) {
453-
bearer.apiKey = await getM2Mtoken();
504+
bearer.apiKey = await getAccessToken();
454505
logger.debug(`Cancelling challenge ${id}`);
455506
try {
456507
const response = await new Promise((resolve, reject) => {
@@ -498,7 +549,7 @@ async function assignUserAsRegistrant(topcoderUserId, challengeId) {
498549
* @param {Object} resource the resource resource to remove
499550
*/
500551
async function removeResourceToChallenge(id, resource) {
501-
bearer.apiKey = await getM2Mtoken();
552+
bearer.apiKey = await getAccessToken();
502553
logger.debug(`removing resource from challenge ${id}`);
503554
try {
504555
const response = await new Promise((resolve, reject) => {
@@ -528,7 +579,7 @@ async function removeResourceToChallenge(id, resource) {
528579
* @returns {Array} the resources of challenge
529580
*/
530581
async function getChallengeResources(id) {
531-
const apiKey = await getM2Mtoken();
582+
const apiKey = await getAccessToken();
532583
logger.debug(`getting resource from challenge ${id}`);
533584
try {
534585
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}/resources`, {

0 commit comments

Comments
 (0)