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

Commit 74ea637

Browse files
authored
Merge pull request #98 from topcoder-platform/develop
Version 1.2.2
2 parents 1bb93e1 + 95dbb8e commit 74ea637

19 files changed

+538
-136
lines changed

config/default.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
LOG_LEVEL: process.env.LOG_LEVEL || 'debug',
1717
PARTITION: process.env.PARTITION || 0,
1818
TOPIC: process.env.TOPIC || 'tc-x-events',
19+
TOPIC_NOTIFICATION: process.env.TOPIC_NOTIFICATION || 'notifications.action.create',
1920
KAFKA_OPTIONS: {
2021
connectionString: process.env.KAFKA_URL || 'localhost:9092',
2122
groupId: process.env.KAFKA_GROUP_ID || 'topcoder-x-processor',
@@ -25,6 +26,11 @@ module.exports = {
2526
passphrase: 'secret', // NOTE:* This configuration specifies the private key passphrase used while creating it.
2627
}
2728
},
29+
MAIL_NOTICIATION: {
30+
type: 'tcx.mail_notification',
31+
sendgridTemplateId: 'xxxxxx',
32+
subject: 'Topcoder X Alert'
33+
},
2834
NEW_CHALLENGE_TEMPLATE: process.env.NEW_CHALLENGE_TEMPLATE || {
2935
status: 'Draft'
3036
},
@@ -68,5 +74,10 @@ module.exports = {
6874
ROLE_ID_SUBMITTER: process.env.ROLE_ID_SUBMITTER || '732339e7-8e30-49d7-9198-cccf9451e221',
6975
TYPE_ID_TASK: process.env.TYPE_ID_TASK || 'ecd58c69-238f-43a4-a4bb-d172719b9f31',
7076
DEFAULT_TIMELINE_TEMPLATE_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '53a307ce-b4b3-4d6f-b9a1-3741a58f77e6',
71-
DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825'
77+
DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825',
78+
GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION: 3600 * 2,
79+
GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION: 300,
80+
GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID,
81+
GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET,
82+
GITLAB_OWNER_USER_CALLBACK_URL: process.env.GITLAB_OWNER_USER_CALLBACK_URL
7283
};

configuration.md

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ The following config parameters are supported, they are defined in `config/defau
1616
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
1717
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|
1818
|GITLAB_API_BASE_URL| the URL for gitlab host| defaults to `https://gitlab.com`|
19+
| GITLAB_CLIENT_ID | the GitLab client id | No default - needs to be set up with same value found in topcoder-x-ui |
20+
| GITLAB_CLIENT_SECRET | the GitLab client secret | No default - needs to be set up with same value found in topcoder-x-ui |
21+
| GITLAB_OWNER_USER_CALLBACK_URL | the GitLab callback redirect uri for refreshing copilot token | No default - needs to be set up with same owner user callback value in topcoder-x-ui |
1922
|PAID_ISSUE_LABEL|the label name for paid, should be one of the label configured in topcoder x ui|'tcx_Paid'|
2023
|FIX_ACCEPTED_ISSUE_LABEL|the label name for fix accepted, should be one of the label configured in topcoder x ui|'tcx_FixAccepted'|
2124
|ASSIGNED_ISSUE_LABEL| the label name for assigned, should be one of the label configured in topcoder x ui| 'tcx_Assigned'|

index.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
const config = require('config');
77
const _ = require('lodash');
8-
const kafka = require('./utils/kafka');
8+
const kafkaConsumer = require('./utils/kafka-consumer');
99
const logger = require('./utils/logger');
1010

1111
process.on('uncaughtException', (err) => {
@@ -22,7 +22,9 @@ process.on('unhandledRejection', (err) => {
2222
});
2323

2424
// dump the configuration to logger
25-
const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET'];
25+
const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET',
26+
'GITLAB_CLIENT_ID', 'GITLAB_CLIENT_SECRET'];
27+
2628
/**
2729
* Print configs to logger
2830
* @param {Object} params the config params
@@ -55,4 +57,4 @@ dumpConfigs(config, 0);
5557
logger.debug('--- End of List of Configurations ---');
5658

5759
// run the server
58-
kafka.run();
60+
kafkaConsumer.run();

models/Project.js

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ const schema = new Schema({
2323
type: Number,
2424
required: true
2525
},
26+
tags: {
27+
type: String,
28+
required: true,
29+
default: ''
30+
},
2631
rocketChatWebhook: {type: String, required: false},
2732
rocketChatChannelName: {type: String, required: false},
2833
archived: {type: String, required: true},

services/ChallengeService.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2022 TopCoder, Inc. All rights reserved.
3+
*/
4+
'use strict';
5+
6+
/**
7+
* This service processes incoming pure challenge events.
8+
*
9+
* @author TCSCODER
10+
* @version 1.0
11+
*/
12+
const _ = require('lodash');
13+
const Joi = require('joi');
14+
const logger = require('../utils/logger');
15+
const topcoderApiHelper = require('../utils/topcoder-api-helper');
16+
const dbHelper = require('../utils/db-helper');
17+
18+
/**
19+
* Update challenge tags
20+
* @param {Object} event the event
21+
*/
22+
async function handleChallengeTagsUpdate(event) {
23+
const tags = event.data.tags.split(',');
24+
await Promise.all(
25+
event.data.challengeUUIDsList.map(async (challengeUUIDs) => {
26+
if (_.isString(challengeUUIDs)) { // repoUrl
27+
challengeUUIDs = await dbHelper.queryChallengeUUIDsByRepoUrl(challengeUUIDs);
28+
}
29+
return challengeUUIDs.map(async (challengeUUID) => await topcoderApiHelper.updateChallenge(challengeUUID, {tags}));
30+
}).reduce((a, b) => _.concat(a, b), [])
31+
).then((resps) => {
32+
logger.debug(`handleChallengeTagsUpdate updated ${_.size(resps)} challenges successfully.`);
33+
}).catch((err) => {
34+
logger.error(`handleChallengeTagsUpdate failed. Internal Error: ${err}`);
35+
throw new Error(`handleChallengeTagsUpdate failed. Internal Error: ${err}`);
36+
});
37+
}
38+
39+
/**
40+
* Process pure challenge event.
41+
* @param {Object} event the event
42+
*/
43+
async function process(event) {
44+
Joi.attempt(event, process.schema);
45+
46+
if (event.event === 'challengeTags.update') {
47+
await handleChallengeTagsUpdate(event);
48+
}
49+
}
50+
51+
process.schema = Joi.object().keys({
52+
event: Joi.string().valid('challengeTags.update').required(),
53+
data: Joi.object().keys({
54+
challengeUUIDsList: Joi.array().items(
55+
Joi.alternatives().try(Joi.string(), Joi.array().items(Joi.string()))
56+
).required(),
57+
tags: Joi.string().required()
58+
}).required(),
59+
retryCount: Joi.number().integer().default(0).optional()
60+
});
61+
62+
63+
module.exports = {
64+
process
65+
};
66+
67+
logger.buildService(module.exports);

services/CopilotPaymentService.js

+1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ async function handlePaymentAdd(event, payment) {
194194
const newChallenge = {
195195
name: challengeTitle,
196196
projectId: project.tcDirectId,
197+
tags: !!project.tags ? project.tags.split(',') : [],
197198
detailedRequirements: challengeRequirements,
198199
prizes: [payment.amount],
199200
reviewType: 'INTERNAL'

services/GithubService.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async function _authenticate(accessToken) {
5353
});
5454
return octokit.rest;
5555
} catch (err) {
56-
throw errors.convertGitHubError(err, 'Failed to authenticate to Github using access token of copilot.');
56+
throw errors.handleGitHubError(err, 'Failed to authenticate to Github using access token of copilot.');
5757
}
5858
}
5959

@@ -75,7 +75,7 @@ async function _removeAssignees(github, owner, repo, number, assignees) {
7575
assignees
7676
});
7777
} catch (err) {
78-
throw errors.convertGitHubError(err, 'Error occurred during remove assignees from issue.');
78+
throw errors.handleGitHubError(err, 'Error occurred during remove assignees from issue.');
7979
}
8080
}
8181

@@ -93,6 +93,17 @@ async function _getUsernameById(id) {
9393
return user ? user.login : null;
9494
}
9595

96+
/**
97+
* Get github issue url
98+
* @param {String} repoPath the repo path
99+
* @param {Number} number the issue number
100+
* @returns {String} the url
101+
* @private
102+
*/
103+
function _getIssueUrl(repoPath, number) {
104+
return `https://github.com/${repoPath}/issues/${number}`;
105+
}
106+
96107
/**
97108
* updates the title of github issue
98109
* @param {Object} copilot the copilot
@@ -107,7 +118,7 @@ async function updateIssue(copilot, repoFullName, number, title) {
107118
try {
108119
await github.issues.update({owner, repo, issue_number: number, title});
109120
} catch (err) {
110-
throw errors.convertGitHubError(err, 'Error occurred during updating issue.');
121+
throw errors.handleGitHubError(err, 'Error occurred during updating issue.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
111122
}
112123
logger.debug(`Github issue title is updated for issue number ${number}`);
113124
}
@@ -139,7 +150,7 @@ async function assignUser(copilot, repoFullName, number, user) {
139150
}
140151
await github.issues.addAssignees({owner, repo, issue_number: number, assignees: [user]});
141152
} catch (err) {
142-
throw errors.convertGitHubError(err, 'Error occurred during assigning issue user.');
153+
throw errors.handleGitHubError(err, 'Error occurred during assigning issue user.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
143154
}
144155
logger.debug(`Github issue with number ${number} is assigned to ${user}`);
145156
}
@@ -184,7 +195,7 @@ async function createComment(copilot, repoFullName, number, body) {
184195
body = helper.prepareAutomatedComment(body, copilot);
185196
await github.issues.createComment({owner, repo, issue_number: number, body});
186197
} catch (err) {
187-
throw errors.convertGitHubError(err, 'Error occurred during creating comment on issue.');
198+
throw errors.handleGitHubError(err, 'Error occurred during creating comment on issue.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
188199
}
189200
logger.debug(`Github comment is added on issue with message: "${body}"`);
190201
}
@@ -262,7 +273,7 @@ async function markIssueAsPaid(copilot, repoFullName, number, challengeUUID, exi
262273
const body = helper.prepareAutomatedComment(commentMessage, copilot);
263274
await github.issues.createComment({owner, repo, issue_number: number, body});
264275
} catch (err) {
265-
throw errors.convertGitHubError(err, 'Error occurred during updating issue as paid.');
276+
throw errors.handleGitHubError(err, 'Error occurred during updating issue as paid.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
266277
}
267278
logger.debug(`Github issue title is updated for as paid and fix accepted for ${number}`);
268279
}
@@ -291,7 +302,7 @@ async function changeState(copilot, repoFullName, number, state) {
291302
try {
292303
await github.issues.update({owner, repo, issue_number: number, state});
293304
} catch (err) {
294-
throw errors.convertGitHubError(err, 'Error occurred during updating status of issue.');
305+
throw errors.handleGitHubError(err, 'Error occurred during updating status of issue.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
295306
}
296307
logger.debug(`Github issue state is updated to '${state}' for issue number ${number}`);
297308
}
@@ -317,7 +328,7 @@ async function addLabels(copilot, repoFullName, number, labels) {
317328
try {
318329
await github.issues.update({owner, repo, issue_number: number, labels});
319330
} catch (err) {
320-
throw errors.convertGitHubError(err, 'Error occurred during adding label in issue.');
331+
throw errors.handleGitHubError(err, 'Error occurred during adding label in issue.', copilot.topcoderUsername, _getIssueUrl(repoFullName, number));
321332
}
322333
logger.debug(`Github issue is updated with new labels for ${number}`);
323334
}

0 commit comments

Comments
 (0)