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

Use old token. Change prod challenge api to v3. Clean lint. #34

Merged
merged 1 commit into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ The following config parameters are supported, they are defined in `config/defau
| PARTITION | the kafka partition | 0|
| KAFKA_OPTIONS | the connection option for kafka | see below about KAFKA options |
|TC_DEV_ENV| the flag whether to use topcoder development api or production| false|
| TC_AUTHN_URL | the Topcoder authentication url | https://topcoder-dev.auth0.com/oauth/ro |
| 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` |
| TC_AUTHZ_URL | the Topcoder authorization url | https://api.topcoder-dev.com/v3/authorizations |
| NEW_CHALLENGE_TEMPLATE | the body template for new challenge request. You can change the subTrack, reviewTypes, technologies, .. here | see `default.js` |
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|
Expand Down
13 changes: 13 additions & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ module.exports = {
}
},
TC_DEV_ENV: process.env.NODE_ENV === 'production' ? false : true,
TC_AUTHN_URL: process.env.TC_AUTHN_URL || 'https://topcoder-dev.auth0.com/oauth/ro',
TC_AUTHN_REQUEST_BODY: {
username: process.env.TC_USERNAME || 'mess',
password: process.env.TC_PASSWORD || 'appirio123',
client_id: process.env.TC_CLIENT_ID || 'JFDo7HMkf0q2CkVFHojy3zHWafziprhT',
sso: false,
scope: 'openid profile offline_access',
response_type: 'token',
connection: process.env.CLIENT_V2CONNECTION || 'TC-User-Database',
grant_type: 'password',
device: 'Browser'
},
TC_AUTHZ_URL: process.env.TC_AUTHZ_URL || 'https://api.topcoder-dev.com/v3/authorizations',
NEW_CHALLENGE_TEMPLATE: process.env.NEW_CHALLENGE_TEMPLATE || {
milestoneId: 1,
subTrack: 'FIRST_2_FINISH',
Expand Down
3 changes: 3 additions & 0 deletions configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ The following config parameters are supported, they are defined in `config/defau
|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 |
|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|
|TC_DEV_ENV| the flag whether to use topcoder development api or production| false|
| TC_AUTHN_URL | the Topcoder authentication url | https://topcoder-dev.auth0.com/oauth/ro |
| 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` |
| TC_AUTHZ_URL | the Topcoder authorization url | https://api.topcoder-dev.com/v3/authorizations |
| NEW_CHALLENGE_TEMPLATE | the body template for new challenge request. You can change the subTrack, reviewTypes, technologies, .. here | see `default.js` |
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
},
"homepage": "https://gitlab.com/luettich/processor#README",
"dependencies": {
"@topcoder-platform/topcoder-api-challenges-v4-wrapper": "^1.0.5",
"@topcoder-platform/topcoder-api-challenges-v4-wrapper-dev": "^1.0.5",
"axios": "^0.19.0",
"circular-json": "^0.5.7",
Expand All @@ -43,6 +42,7 @@
"node-gitlab-api": "^2.2.8",
"nodemailer": "^4.6.7",
"tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.6.3",
"topcoder-api-challenges": "^1.0.6",
"topcoder-api-projects": "^1.0.1",
"topcoder-dev-api-projects": "^1.0.1",
"topcoder-healthcheck-dropin": "^1.0.3",
Expand Down
18 changes: 9 additions & 9 deletions services/IssueService.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,13 @@ async function handleIssueAssignment(event, issue, force = false) {
dbIssue = await ensureChallengeExists(event, issue);

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

// Handle multiple assignees. TC-X allows only one assignee.
if (event.data.issue.assignees && event.data.issue.assignees.length > 1) {
const comment = 'Topcoder-X only supports a single assignee on a ticket to avoid issues with payment';
Expand Down Expand Up @@ -313,7 +313,7 @@ async function handleIssueUpdate(event, issue) {
dbIssue = await ensureChallengeExists(event, issue, false);

if (!dbIssue) {
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
// Handle it for rescheduling.
await eventService.handleEventGracefully(event, issue, err);
Expand Down Expand Up @@ -363,9 +363,9 @@ async function handleIssueClose(event, issue) {
let dbIssue;
try {
dbIssue = await ensureChallengeExists(event, issue);

if (!dbIssue) {
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
// Handle it for rescheduling.
await eventService.handleEventGracefully(event, issue, err);
Expand All @@ -376,11 +376,11 @@ async function handleIssueClose(event, issue) {

// if the issue has payment success or payment pending status, we'll ignore this process.
if (dbIssue && dbIssue.status === 'challenge_payment_successful') {
logger.debug(`Ignoring close issue processing. The issue has challenge_payment_successful.`);
logger.debug('Ignoring close issue processing. The issue has challenge_payment_successful.');
return;
}
if (dbIssue && dbIssue.status === 'challenge_payment_pending') {
logger.debug(`Ignoring close issue processing. The issue has challenge_payment_pending.`);
logger.debug('Ignoring close issue processing. The issue has challenge_payment_pending.');
return;
}

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

if (!dbIssue) {
const err = errors.internalDependencyError(`Can't find the issue in DB. It's not found or not accessible`);
const err = errors.internalDependencyError('Can\'t find the issue in DB. It\'s not found or not accessible');
// The dbissue is not found, the db is not accessible, or the issue is still in creation process.
// Handle it for rescheduling.
await eventService.handleEventGracefully(event, issue, err);
Expand Down
4 changes: 2 additions & 2 deletions utils/db-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ async function updateMany(model, collection) {
return reject(err);
}

resolve(result);
return resolve(result);
});
});
}
Expand Down Expand Up @@ -142,7 +142,7 @@ async function remove(Model, queryParams) {
return reject(err);
}

resolve(dbItem);
return resolve(dbItem);
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion utils/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
* @version 1.0
*/
'use strict';
const config = require('config');
const util = require('util');
const config = require('config');
const _ = require('lodash');
const winston = require('winston');
const getParams = require('get-parameter-names');
Expand Down
97 changes: 74 additions & 23 deletions utils/topcoder-api-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
'use strict';

const config = require('config');
const jwtDecode = require('jwt-decode');
const axios = require('axios');
const _ = require('lodash');
const moment = require('moment');
const circularJSON = require('circular-json');

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

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

let topcoderApiProjects = require('topcoder-api-projects');
let topcoderApiChallenges = require('@topcoder-platform/topcoder-api-challenges-v4-wrapper');
let topcoderApiChallenges = require('topcoder-api-challenges');

const topcoderDevApiProjects = require('topcoder-dev-api-projects');
const topcoderDevApiChallenges = require('@topcoder-platform/topcoder-api-challenges-v4-wrapper-dev');
Expand All @@ -37,12 +38,16 @@ if (config.TC_DEV_ENV) {
topcoderApiProjects = topcoderDevApiProjects;
topcoderApiChallenges = topcoderDevApiChallenges;
}

// Cache the access token
let cachedAccessToken;

// Init the API instances
const projectsClient = topcoderApiProjects.ApiClient.instance;
const challengesClient = topcoderApiChallenges.ApiClient.instance;

//Timeout increase to 5 minutes
challengesClient.timeout=300000;
// Timeout increase to 5 minutes
challengesClient.timeout = 300000;

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

/**
* Function to get M2M token
* @returns {Promise} The promised token
* Authenticate with Topcoder API and get the access token.
* @returns {String} the access token issued by Topcoder
* @private
*/
async function getM2Mtoken() {
return await m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET);
async function getAccessToken() {
// Check the cached access token
if (cachedAccessToken) {
const decoded = jwtDecode(cachedAccessToken);
if (decoded.iat > new Date().getTime()) {
// Still not expired, just use it
return cachedAccessToken;
}
}

// Authenticate
const v2Response = await axios.post(config.TC_AUTHN_URL, config.TC_AUTHN_REQUEST_BODY);
const v2IdToken = _.get(v2Response, 'data.id_token');
const v2RefreshToken = _.get(v2Response, 'data.refresh_token');

if (!v2IdToken || !v2RefreshToken) {
throw new Error(`cannot authenticate with topcoder: ${config.TC_AUTHN_URL}`);
}

// Authorize
const v3Response = await axios.post(
config.TC_AUTHZ_URL, {
param: {
externalToken: v2IdToken,
refreshToken: v2RefreshToken
}
}, {
headers: {
authorization: `Bearer ${v2IdToken}`
}
});

cachedAccessToken = _.get(v3Response, 'data.result.content.token');

if (!cachedAccessToken) {
throw new Error(`cannot authorize with topcoder: ${config.TC_AUTHZ_URL}`);
}

return cachedAccessToken;
}

// /**
// * Function to get M2M token
// * @returns {Promise} The promised token
// */
// async function getM2Mtoken() {
// return await m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET);
// }

/**
* Create a new project.
* @param {String} projectName the project name
* @returns {Number} the created project id
*/
async function createProject(projectName) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
// eslint-disable-next-line new-cap
const projectBody = new topcoderApiProjects.ProjectRequestBody.constructFromObject({
projectName
Expand Down Expand Up @@ -96,7 +147,7 @@ async function createProject(projectName) {
* @returns {Number} the created challenge id
*/
async function createChallenge(challenge) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
const start = new Date();
const startTime = moment(start).toISOString();
const end = moment(start).add(config.NEW_CHALLENGE_DURATION_IN_DAYS, 'days').toISOString();
Expand Down Expand Up @@ -137,7 +188,7 @@ async function createChallenge(challenge) {
* @param {Object} challenge the challenge to update
*/
async function updateChallenge(id, challenge) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`Updating challenge ${id} with ${circularJSON.stringify(challenge)}`);
// eslint-disable-next-line new-cap
const challengeBody = new topcoderApiChallenges.UpdateChallengeBodyParam.constructFromObject({
Expand Down Expand Up @@ -175,7 +226,7 @@ async function updateChallenge(id, challenge) {
* @param {Number} id the challenge id
*/
async function activateChallenge(id) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`Activating challenge ${id}`);
try {
const response = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -215,7 +266,7 @@ async function getChallengeById(id) {
if (!_.isNumber(id)) {
throw new Error('The challenge id must valid number');
}
const apiKey = await getM2Mtoken();
const apiKey = await getAccessToken();
logger.debug('Getting topcoder challenge details');
try {
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}`, {
Expand Down Expand Up @@ -246,7 +297,7 @@ async function getChallengeById(id) {
* @param {Number} winnerId the winner id
*/
async function closeChallenge(id, winnerId) {
const apiKey = await getM2Mtoken();
const apiKey = await getAccessToken();
logger.debug(`Closing challenge ${id}`);
try {
const response = await axios.post(`${challengesClient.basePath}/challenges/${id}/close?winnerId=${winnerId}`, null, {
Expand Down Expand Up @@ -276,7 +327,7 @@ async function closeChallenge(id, winnerId) {
* @returns {Number} the billing account id
*/
async function getProjectBillingAccountId(id) {
const apiKey = await getM2Mtoken();
const apiKey = await getAccessToken();
logger.debug(`Getting project billing detail ${id}`);
try {
const response = await axios.get(`${projectsClient.basePath}/direct/projects/${id}`, {
Expand Down Expand Up @@ -308,7 +359,7 @@ async function getProjectBillingAccountId(id) {
* @returns {Number} the user id
*/
async function getTopcoderMemberId(handle) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
try {
const response = await axios.get(`${projectsClient.basePath}/members/${handle}`);
const statusCode = response ? response.status : null;
Expand All @@ -327,7 +378,7 @@ async function getTopcoderMemberId(handle) {
* @param {Object} resource the resource resource to add
*/
async function addResourceToChallenge(id, resource) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`adding resource to challenge ${id}`);
try {
const response = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -368,7 +419,7 @@ async function getResourcesFromChallenge(id) {
if (!_.isNumber(id)) {
throw new Error('The challenge id must valid number');
}
const apiKey = await getM2Mtoken();
const apiKey = await getAccessToken();
logger.debug(`fetch resource from challenge ${id}`);
try {
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}/resources`, {
Expand Down Expand Up @@ -413,7 +464,7 @@ async function roleAlreadySet(id, role) {
* @param {Object} resource the resource resource to remove
*/
async function unregisterUserFromChallenge(id) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`removing resource from challenge ${id}`);
try {
const response = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -450,7 +501,7 @@ async function unregisterUserFromChallenge(id) {
* @param {Number} id the challenge id
*/
async function cancelPrivateContent(id) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`Cancelling challenge ${id}`);
try {
const response = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -498,7 +549,7 @@ async function assignUserAsRegistrant(topcoderUserId, challengeId) {
* @param {Object} resource the resource resource to remove
*/
async function removeResourceToChallenge(id, resource) {
bearer.apiKey = await getM2Mtoken();
bearer.apiKey = await getAccessToken();
logger.debug(`removing resource from challenge ${id}`);
try {
const response = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -528,7 +579,7 @@ async function removeResourceToChallenge(id, resource) {
* @returns {Array} the resources of challenge
*/
async function getChallengeResources(id) {
const apiKey = await getM2Mtoken();
const apiKey = await getAccessToken();
logger.debug(`getting resource from challenge ${id}`);
try {
const response = await axios.get(`${challengesClient.basePath}/challenges/${id}/resources`, {
Expand Down