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

File tree

8 files changed

+44
-17
lines changed

8 files changed

+44
-17
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ When an issue is closed it will first check if issue has any assignee or not,
217217
- closes the challenge with winner as assignee
218218
- you can verify the challenge closed in OR (link will be commented in same issue in git host)
219219
- issue label will be updated from configured paid and fix accepted label name
220+
- if there is no fix_accepted label or prize is zero then the challenge will cancelled
220221
221222
You can see following logs
222223
```

config/default.js

+1
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,5 @@ module.exports = {
8989
TC_OR_DETAIL_LINK: process.env.TC_OR_DETAIL_LINK || 'https://software.topcoder-dev.com/review/actions/ViewProjectDetails?pid=',
9090
RETRY_COUNT: process.env.RETRY_COUNT || 3,
9191
RETRY_INTERVAL: process.env.RETRY_INTERVAL || 120000, // 2 minutes
92+
CANCEL_CHALLENGE_INTERVAL: process.env.CANCEL_CHALLENGE_INTERVAL || 20 * 1000,
9293
};

configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ The following config parameters are supported, they are defined in `config/defau
3131
|RETRY_COUNT| the number of times an event should be retried to process| 3|
3232
|RETRY_INTERVAL| the interval at which the event should be retried to process in milliseconds | 120000|
3333
|READY_FOR_REVIEW_ISSUE_LABEL| the label name for ready for review, should be one of the label configured in topcoder x ui|'tcx_ReadyForReview'|
34+
|CANCEL_CHALLENGE_INTERVAL| the time in millisecond after which the challenge will be closed| '24*60*60*1000'|
3435

3536
KAFKA_OPTIONS should be object as described in https://github.com/oleksiyk/kafka#ssl
3637
For using with SSL, the options should be as

models/Issue.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ const schema = new mongoose.Schema({
2727
// From topcoder api
2828
challengeId: {type: Number, required: false, unique: true},
2929
projectId: {type: mongoose.Schema.Types.ObjectId, ref: 'Project'},
30-
status: {type: String}
30+
status: {type: String},
31+
assignedAt: {type: Date, required: false}
3132
});
3233

3334
// Issue number, provider, repositoryId must be unique

models/Project.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ const schema = new mongoose.Schema({
1717
rocketChatWebhook: {type: String, required: false},
1818
rocketChatChannelName: {type: String, required: false},
1919
archived: {type: String, required: true},
20-
username: {type: String, required: true},
21-
secretWebhookKey: {type: String, required: true}
20+
owner: {type: String, required: true},
21+
secretWebhookKey: {type: String, required: true},
22+
copilot: {type: String, required: true}
2223
});
2324

2425
schema.index({tcDirectId: 1});

services/CopilotPaymentService.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async function getExistingChallengeIdIfExists(event, dbPayment) {
6464
// check if there is existing active challenge associated with this project
6565
const existingPayments = await CopilotPayment.findOne({
6666
project: dbPayment.project,
67-
username: event.data.copilot.handle,
67+
username: event.project.copilot,
6868
closed: false,
6969
challengeId: {
7070
$gt: 0
@@ -135,7 +135,7 @@ async function _checkAndReSchedule(event, payment) {
135135
// get all unclosed payments for given project and user
136136
const existingPending = await CopilotPayment.find({
137137
project: payment.project,
138-
username: event.data.copilot.handle,
138+
username: event.project.copilot,
139139
closed: false,
140140
status: 'challenge_creation_pending'
141141
});
@@ -158,7 +158,7 @@ async function _checkAndReSchedule(event, payment) {
158158
* @private
159159
*/
160160
async function handlePaymentAdd(event, payment) {
161-
const copilot = event.data.copilot;
161+
const copilot = {handle: event.project.copilot};
162162

163163
payment = await getExistingChallengeIdIfExists(event, payment);
164164
if (!_.isNil(payment.challengeId)) {
@@ -232,7 +232,7 @@ async function handlePaymentAdd(event, payment) {
232232
* @private
233233
*/
234234
async function handlePaymentUpdate(event, payment) {
235-
const copilot = event.data.copilot;
235+
const copilot = {handle: event.project.copilot};
236236
await _updateChallenge(copilot.handle, payment.project, payment.challengeId);
237237
logger.debug(`updated payment for challenge ${payment.challengeId} successful.`);
238238
}
@@ -245,7 +245,7 @@ async function handlePaymentUpdate(event, payment) {
245245
* @private
246246
*/
247247
async function handlePaymentDelete(event, payment) {
248-
const copilot = event.data.copilot;
248+
const copilot = {handle: event.project.copilot};
249249
await _updateChallenge(copilot.handle, payment.project, payment.challengeId);
250250
logger.debug(`updated payment for challenge ${payment.challengeId} successful.`);
251251
}
@@ -255,11 +255,16 @@ async function handlePaymentDelete(event, payment) {
255255
* @param {Object} event the event
256256
*/
257257
async function handlePaymentUpdates(event) {
258-
const copilot = event.data.copilot;
258+
const projectIds = await Project.find({
259+
$or: [
260+
{owner: event.copilot.handle},
261+
{copilot: event.copilot.handle}
262+
]
263+
}).select('_id');
259264

260265
// get all unclosed payments for current user
261266
const dbPayments = await CopilotPayment.find({
262-
username: copilot.handle,
267+
project: {$in: projectIds},
263268
closed: false
264269
});
265270
if (dbPayments) {
@@ -269,7 +274,7 @@ async function handlePaymentUpdates(event) {
269274
const challengeId = challengeIds[i];
270275
const challengeDetail = await topcoderApiHelper.getChallengeById(challengeId);
271276
if (challengeDetail && challengeDetail.currentStatus === 'Completed') {
272-
await CopilotPayment.updateMany({challengeId, username: copilot.handle, closed: false}, {closed: true});
277+
await CopilotPayment.updateMany({challengeId, closed: false}, {closed: true});
273278
}
274279
}
275280
logger.debug('Success updating payments status.');
@@ -293,6 +298,9 @@ async function process(event) {
293298
if (_.isNil(payment.challengeId)) {
294299
delete payment.challengeId;
295300
}
301+
if (payment.project) {
302+
event.project = await Project.findById(payment.project);
303+
}
296304
if (event.event === 'copilotPayment.add') {
297305
await handlePaymentAdd(event, payment);
298306
} else if (event.event === 'copilotPayment.update') {

services/IssueService.js

+18-4
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ async function handleIssueAssignment(event, issue) {
255255
logger.debug(`Assigning user to challenge: ${userMapping.topcoderUsername}`);
256256
topcoderApiHelper.assignUserAsRegistrant(topcoderUserId, dbIssue.challengeId);
257257
dbIssue.set({
258-
assignee: issue.assignee
258+
assignee: issue.assignee,
259+
assignedAt: new Date()
259260
});
260261
await dbIssue.save();
261262
// remove open for pickup and add assigned
@@ -381,6 +382,7 @@ async function handleIssueClose(event, issue) {
381382
try {
382383
dbIssue = await ensureChallengeExists(event, issue);
383384
if (!event.paymentSuccessful) {
385+
let closeChallenge = false;
384386
// if issue is closed without Fix accepted label
385387
if (!_.includes(event.data.issue.labels, config.FIX_ACCEPTED_ISSUE_LABEL)) {
386388
logger.debug(`This issue ${issue.number} is closed without fix accepted label.`);
@@ -391,9 +393,13 @@ async function handleIssueClose(event, issue) {
391393
} else {
392394
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
393395
}
394-
return;
396+
closeChallenge = true;
397+
}
398+
if (issue.prizes[0] === 0) {
399+
closeChallenge = true;
395400
}
396401

402+
397403
// if issue is closed without assignee then do nothing
398404
if (!event.data.assignee.id) {
399405
logger.debug(`This issue ${issue.number} doesn't have assignee so ignoring this event.`);
@@ -453,7 +459,14 @@ async function handleIssueClose(event, issue) {
453459

454460
// activate challenge
455461
await topcoderApiHelper.activateChallenge(dbIssue.challengeId);
456-
462+
if (closeChallenge) {
463+
logger.debug(`The associated challenge ${dbIssue.challengeId} is scheduled for cancel`);
464+
setTimeout(async () => {
465+
await topcoderApiHelper.cancelPrivateContent(dbIssue.challengeId);
466+
logger.debug(`The challenge ${dbIssue.challengeId} is deleted`);
467+
}, config.CANCEL_CHALLENGE_INTERVAL); //eslint-disable-line
468+
return;
469+
}
457470
logger.debug(`close challenge with winner ${assigneeMember.topcoderUsername}(${winnerId})`);
458471
await topcoderApiHelper.closeChallenge(dbIssue.challengeId, winnerId);
459472
event.paymentSuccessful = true;
@@ -615,7 +628,8 @@ async function handleIssueUnAssignment(event, issue) {
615628
return;
616629
}
617630
dbIssue.set({
618-
assignee: null
631+
assignee: null,
632+
assignedAt: null
619633
});
620634
await dbIssue.save();
621635
}

services/UserService.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ async function getRepositoryCopilot(provider, repoFullName) {
6767
repoUrl: fullRepoUrl
6868
});
6969

70-
if (!project || !project.username) {
70+
if (!project || !project.owner || !project.copilot) {
7171
// throw this repo is not managed by Topcoder x tool
7272
throw new Error(`This repository '${repoFullName}' is not managed by Topcoder X tool.`);
7373
}
7474

7575
const userMapping = await models.UserMapping.findOne({
76-
topcoderUsername: project.username.toLowerCase()
76+
topcoderUsername: project.copilot.toLowerCase()
7777
});
7878

7979
if (!userMapping || (provider === 'github' && !userMapping.githubUserId) || (provider === 'gitlab' && !userMapping.gitlabUserId)) {

0 commit comments

Comments
 (0)