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

changes of challenge #60

Merged
merged 1 commit into from
Nov 3, 2020
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
6 changes: 3 additions & 3 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ module.exports = {
WEBSITE_SECURE: process.env.WEBSITE_SECURE || 'https://topcoderx.topcoder-dev.com',

ROLE_ID_COPILOT: process.env.ROLE_ID_COPILOT || 'cfe12b3f-2a24-4639-9d8b-ec86726f76bd',
ROLE_ID_SUBMITTER: process.env.ROLE_ID_SUBMITTER || '732339e7-8e30-49d7-9198-cccf9451e221',
ROLE_ID_SUBMITTER: process.env.ROLE_ID_SUBMITTER || '0bc7e20e-cf08-4051-b6e5-421d69e492ac',
TYPE_ID_FIRST2FINISH: process.env.TYPE_ID_FIRST2FINISH || '927abff4-7af9-4145-8ba1-577c16e64e2e',
DEFAULT_TIMELINE_TEMPLATE_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c',
DEFAULT_TRACK_ID : process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825'
DEFAULT_TIMELINE_TEMPLATE_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '6969125a-a12f-4b89-8de6-e66b0056f36b',
DEFAULT_TRACK_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825'
};
15 changes: 14 additions & 1 deletion constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,22 @@ const CHALLENGE_STATUS = {

const SERVICE_ERROR_STATUS = 500;

// issue status
const ISSUE_STATUS = {
CHALLENGE_CANCELLED: 'challenge_cancelled',
CHALLENGE_CREATION_PENDING: 'challenge_creation_pending',
CHALLENGE_CREATION_SUCCESSFUL: 'challenge_creation_successful',
CHALLENGE_CREATION_FAILED: 'challenge_creation_failed',
CHALLENGE_CREATION_RETRIED: 'challenge_creation_retried',
CHALLENGE_PAYMENT_SUCCESSFUL: 'challenge_payment_successful',
CHALLENGE_PAYMENT_PENDING: 'challenge_payment_pending',
CHALLENGE_PAYMENT_FAILED: 'challenge_payment_failed'
};

module.exports = {
USER_ROLES,
USER_TYPES,
SERVICE_ERROR_STATUS,
CHALLENGE_STATUS
CHALLENGE_STATUS,
ISSUE_STATUS
};
8 changes: 4 additions & 4 deletions services/CopilotPaymentService.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ function updateChallengeDetails(payments) {
*/
async function getExistingChallengeIdIfExists(event, dbPayment) {
// check if there is existing active challenge associated with this project
let existingPayments = await dbHelper.scan(models.CopilotPayment, {
const existingPayments = await dbHelper.scan(models.CopilotPayment, {
project: {eq: dbPayment.project},
username: {eq: event.project.copilot},
closed: {eq: 'false'}
});

const payment = _.find(existingPayments, x => x.challengeUUID);
const payment = _.find(existingPayments, (x) => x.challengeUUID);

// if no existing challenge found then it will be created by processor
if (payment) {
// update db payment
Expand Down Expand Up @@ -286,7 +286,7 @@ async function handlePaymentUpdates(event) {
},
ExpressionAttributeValues: filterValues
});

if (dbPayments) {
const challengeIds = _(dbPayments).map('challengeUUID').uniq().filter(_.isString)
.value();
Expand Down
48 changes: 24 additions & 24 deletions services/GithubService.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async function _removeAssignees(github, owner, repo, number, assignees) {
* @returns {string} username if found
*/
async function _getUsernameById(github, id) {
const user = await github.users.getById({ id });
const user = await github.users.getById({id});
return user ? user.data.login : null;
}

Expand All @@ -98,11 +98,11 @@ async function _getUsernameById(github, id) {
* @param {string} title new title
*/
async function updateIssue(copilot, repoFullName, number, title) {
Joi.attempt({ copilot, repoFullName, number, title }, updateIssue.schema);
Joi.attempt({copilot, repoFullName, number, title}, updateIssue.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({ owner, repo, number, title });
await github.issues.edit({owner, repo, number, title});
} catch (err) {
throw errors.convertGitHubError(err, 'Error occurred during updating issue.');
}
Expand All @@ -124,17 +124,17 @@ updateIssue.schema = {
* @param {string} user the user login of assignee
*/
async function assignUser(copilot, repoFullName, number, user) {
Joi.attempt({ copilot, repoFullName, number, user }, assignUser.schema);
Joi.attempt({copilot, repoFullName, number, user}, assignUser.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
const issue = await github.issues.get({ owner, repo, number });
const issue = await github.issues.get({owner, repo, number});

const oldAssignees = _(issue.data.assignees).map('login').without(user).value();
if (oldAssignees && oldAssignees.length > 0) {
await _removeAssignees(github, owner, repo, number, oldAssignees);
}
await github.issues.addAssigneesToIssue({ owner, repo, number, assignees: [user] });
await github.issues.addAssigneesToIssue({owner, repo, number, assignees: [user]});
} catch (err) {
throw errors.convertGitHubError(err, 'Error occurred during assigning issue user.');
}
Expand All @@ -156,10 +156,10 @@ assignUser.schema = {
* @param {string} user the user login of assignee
*/
async function removeAssign(copilot, repoFullName, number, user) {
Joi.attempt({ copilot, repoFullName, number, user }, removeAssign.schema);
Joi.attempt({copilot, repoFullName, number, user}, removeAssign.schema);

const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
await _removeAssignees(github, owner, repo, number, [user]);
logger.debug(`Github user ${user} is unassigned from issue number ${number}`);
}
Expand All @@ -174,12 +174,12 @@ removeAssign.schema = assignUser.schema;
* @param {string} body the comment body text
*/
async function createComment(copilot, repoFullName, number, body) {
Joi.attempt({ copilot, repoFullName, number, body }, createComment.schema);
Joi.attempt({copilot, repoFullName, number, body}, createComment.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
body = helper.prepareAutomatedComment(body, copilot);
await github.issues.createComment({ owner, repo, number, body });
await github.issues.createComment({owner, repo, number, body});
} catch (err) {
throw errors.convertGitHubError(err, 'Error occurred during creating comment on issue.');
}
Expand All @@ -200,7 +200,7 @@ createComment.schema = {
* @returns {string} the username if found else null
*/
async function getUsernameById(copilot, userId) {
Joi.attempt({ copilot, userId }, getUsernameById.schema);
Joi.attempt({copilot, userId}, getUsernameById.schema);
const github = await _authenticate(copilot.accessToken);
const login = await _getUsernameById(github, userId);
return login;
Expand All @@ -218,9 +218,9 @@ getUsernameById.schema = {
* @returns {Number} the user id if found else null
*/
async function getUserIdByLogin(copilot, login) {
Joi.attempt({ copilot, login }, getUserIdByLogin.schema);
Joi.attempt({copilot, login}, getUserIdByLogin.schema);
const github = await _authenticate(copilot.accessToken);
const user = await github.users.getForUser({ username: login });
const user = await github.users.getForUser({username: login});
return user.data ? user.data.id : null;
}

Expand All @@ -243,11 +243,11 @@ getUserIdByLogin.schema = {
async function markIssueAsPaid(copilot, repoFullName, number, challengeUUID, existLabels, winner, createCopilotPayments) { // eslint-disable-line max-params
Joi.attempt({copilot, repoFullName, number, challengeUUID, existLabels, winner, createCopilotPayments}, markIssueAsPaid.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
const labels = _(existLabels).filter((i) => i !== config.FIX_ACCEPTED_ISSUE_LABEL)
.push(config.FIX_ACCEPTED_ISSUE_LABEL, config.PAID_ISSUE_LABEL).value();
try {
await github.issues.edit({ owner, repo, number, labels });
await github.issues.edit({owner, repo, number, labels});
let commentMessage = '';
commentMessage += `Payment task has been updated: ${config.TC_URL}/challenges/${challengeUUID}\n`;
commentMessage += '*Payments Complete*\n';
Expand Down Expand Up @@ -283,11 +283,11 @@ markIssueAsPaid.schema = {
* @param {string} state new state
*/
async function changeState(copilot, repoFullName, number, state) {
Joi.attempt({ copilot, repoFullName, number, state }, changeState.schema);
Joi.attempt({copilot, repoFullName, number, state}, changeState.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({ owner, repo, number, state });
await github.issues.edit({owner, repo, number, state});
} catch (err) {
throw errors.convertGitHubError(err, 'Error occurred during updating status of issue.');
}
Expand All @@ -309,11 +309,11 @@ changeState.schema = {
* @param {Number} labels the challenge id
*/
async function addLabels(copilot, repoFullName, number, labels) {
Joi.attempt({ copilot, repoFullName, number, labels }, addLabels.schema);
Joi.attempt({copilot, repoFullName, number, labels}, addLabels.schema);
const github = await _authenticate(copilot.accessToken);
const { owner, repo } = _parseRepoUrl(repoFullName);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({ owner, repo, number, labels });
await github.issues.edit({owner, repo, number, labels});
} catch (err) {
throw errors.convertGitHubError(err, 'Error occurred during adding label in issue.');
}
Expand Down
54 changes: 40 additions & 14 deletions services/IssueService.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,27 @@ async function ensureChallengeExists(event, issue, create = true) {
logger.debugWithContext(`DB Issue number: ${issue.number}`, event, issue);
logger.debugWithContext(`DB Issue provider: ${issue.provider}`, event, issue);
logger.debugWithContext(`DB Issue repository: ${issue.repositoryId}`, event, issue);
if (dbIssue && dbIssue.status === 'challenge_creation_pending') {

if (dbIssue && dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_CREATION_PENDING) {
logger.debugWithContext('dbIssue is PENDING', event, issue);
throw errors.internalDependencyError(`Challenge for the updated issue ${issue.number} is creating, rescheduling this event`);
}
if (dbIssue && dbIssue.status === 'challenge_creation_failed') {
const hasOpenForPickupLabel = _(issue.labels).includes(config.OPEN_FOR_PICKUP_ISSUE_LABEL);
if (dbIssue && dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_CREATION_FAILED && hasOpenForPickupLabel) {
// remove issue from db
await dbHelper.removeIssue(models.Issue, issue.repositoryId, issue.number, issue.provider);
dbIssue = null;
}
if (dbIssue && dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_CANCELLED) {
dbIssue = null;
}

if (!dbIssue && create) {
logger.debugWithContext('dbIssue is NULL, process to create new record and challenge', event, issue);

await handleIssueCreate(event, issue, true);
dbIssue = await dbHelper.queryOneIssue(models.Issue, issue.repositoryId, issue.number, issue.provider);
logger.debugWithContext(`dbIssue is CREATED ${dbIssue ? 'Succesfully' : 'Failed'}`, event, issue);
logger.debugWithContext(`dbIssue is CREATED ${dbIssue ? 'Successfully' : 'Failed'}`, event, issue);
}
return dbIssue;
}
Expand Down Expand Up @@ -384,26 +389,39 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
event.dbIssue = dbIssue;

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

if (!event.paymentSuccessful) {
let closeChallenge = false;
// if issue is closed without Fix accepted label
if (!_.includes(event.data.issue.labels, config.FIX_ACCEPTED_ISSUE_LABEL) || _.includes(event.data.issue.labels, config.CANCELED_ISSUE_LABEL)) {
// if issue is closed without Fix accepted and cancel label
if (!_.includes(event.data.issue.labels, config.FIX_ACCEPTED_ISSUE_LABEL) && !_.includes(event.data.issue.labels, config.CANCELED_ISSUE_LABEL)) {
logger.debugWithContext(`This issue ${issue.number} is closed without fix accepted label.`, event, issue);
let comment = 'This ticket was not processed for payment. If you would like to process it for payment,';
comment += ' please reopen it, add the ```' + config.FIX_ACCEPTED_ISSUE_LABEL + '``` label, and then close it again';// eslint-disable-line
await gitHelper.createComment(event, issue.number, comment);
closeChallenge = true;
}

// if issue is close with cancelled label
if (_.includes(event.data.issue.labels, config.CANCELED_ISSUE_LABEL)) {
const comment = `Challenge ${dbIssue.challengeUUID} has been cancelled`;
await topcoderApiHelper.cancelPrivateContent(dbIssue.challengeUUID);
await gitHelper.createComment(event, issue.number, comment);
// update the issue status to payment pending to prevent double processing.
await dbHelper.update(models.Issue, dbIssue.id, {
status: constants.ISSUE_STATUS.CHALLENGE_CANCELLED,
updatedAt: new Date()
});
closeChallenge = true;
}

if (issue.prizes[0] === 0) {
closeChallenge = true;
}
Expand Down Expand Up @@ -437,7 +455,7 @@ async function handleIssueClose(event, issue) { // eslint-disable-line

// update the issue status to payment pending to prevent double processing.
await dbHelper.update(models.Issue, dbIssue.id, {
status: 'challenge_payment_pending',
status: constants.ISSUE_STATUS.CHALLENGE_PAYMENT_PENDING,
updatedAt: new Date()
});

Expand Down Expand Up @@ -503,7 +521,7 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
// update the issue status to payment failed
if (!event.paymentSuccessful && dbIssue && dbIssue.id) {
await dbHelper.update(models.Issue, dbIssue.id, {
status: 'challenge_payment_failed',
status: constants.ISSUE_STATUS.CHALLENGE_PAYMENT_FAILED,
updatedAt: new Date()
});
}
Expand All @@ -520,7 +538,7 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
.value();
dbIssue = await dbHelper.update(models.Issue, dbIssue.id, {
labels,
status: 'challenge_payment_successful',
status: constants.ISSUE_STATUS.CHALLENGE_PAYMENT_SUCCESSFUL,
updatedAt: new Date()
});
await gitHelper.markIssueAsPaid(event, issue.number, dbIssue.challengeUUID, labels, event.assigneeMember.topcoderUsername,
Expand Down Expand Up @@ -552,7 +570,7 @@ async function handleIssueCreate(event, issue, forceAssign = false) {
// Check if duplicated
let dbIssue = await dbHelper.queryOneIssue(models.Issue, issue.repositoryId, issue.number, issue.provider);

if (dbIssue) {
if (dbIssue && dbIssue.status !== constants.ISSUE_STATUS.CHALLENGE_CANCELLED) {
throw new Error(
`Issue ${issue.number} is already in ${dbIssue.status}`);
}
Expand All @@ -573,9 +591,17 @@ async function handleIssueCreate(event, issue, forceAssign = false) {
// create issue with challenge creation pending
const issueObject = _.assign({}, _.omit(issue, 'assignee'), {
id: helper.generateIdentifier(),
status: 'challenge_creation_pending'
status: constants.ISSUE_STATUS.CHALLENGE_CREATION_PENDING
});
dbIssue = await dbHelper.create(models.Issue, issueObject);

if (!dbIssue) {
dbIssue = await dbHelper.create(models.Issue, issueObject);
} else if (dbIssue && dbIssue.status !== 'challenge_cancelled') {
await dbHelper.update(models.Issue, dbIssue.id, {
status: constants.ISSUE_STATUS.CHALLENGE_CREATION_PENDING,
updatedAt: new Date()
});
}

const projectId = project.tcDirectId;

Expand All @@ -593,7 +619,7 @@ async function handleIssueCreate(event, issue, forceAssign = false) {
// update db payment
await dbHelper.update(models.Issue, dbIssue.id, {
challengeUUID: issue.challengeUUID,
status: 'challenge_creation_successful',
status: constants.ISSUE_STATUS.CHALLENGE_CREATION_SUCCESSFUL,
updatedAt: new Date()
});
} catch (e) {
Expand Down
6 changes: 3 additions & 3 deletions utils/topcoder-api-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ async function cancelPrivateContent(id) {

loggerFile.info(`EndPoint: PATCH /challenges/${id}, PATCH parameters: { status: '${constants.CHALLENGE_STATUS.CANCELED}' }, Status Code:null,
Error: 'Failed to cancel challenge.', Details: ${circularJSON.stringify(err)}`);
throw errors.convertTopcoderApiError(err, 'Failed to activate challenge.');
throw errors.convertTopcoderApiError(err, 'Failed to cancel challenge.');
}
}

Expand Down Expand Up @@ -426,9 +426,9 @@ async function removeResourceToChallenge(id, handle, roleId) {
DELETE parameters: null, Status Code:${statusCode}, Response:${circularJSON.stringify(response.data)}`);
} catch (err) {
loggerFile.info(`EndPoint: DELETE /resources, DELETE parameters: null, Status Code:null,
Error: 'Failed to add resource to the challenge.', Details: ${circularJSON.stringify(err)}`);
Error: 'Failed to remove resource from the challenge.', Details: ${circularJSON.stringify(err)}`);
logger.error(`Response Data: ${JSON.stringify(err.response.data)}`);
throw errors.convertTopcoderApiError(err, 'Failed to add resource to the challenge.');
throw errors.convertTopcoderApiError(err, 'Failed to remove resource from the challenge.');
}
}

Expand Down