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

fix for #topcoder-platform/topcoder-x-ui/issues/36 #7

Merged
merged 4 commits into from
Jul 25, 2018
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
81 changes: 47 additions & 34 deletions services/GithubService.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ const copilotUserSchema = Joi.object().keys({
topcoderUsername: Joi.string()
}).required();

/**
* parse the repository name and repoFullName owner
* @param {String} fullName the full repository name
* @returns {Object} the parsed data
* @private
*/
function _parseRepoUrl(fullName) {
const results = fullName.split('/');
const repo = results[results.length - 1];
const owner = _(results).slice(0, results.length - 1).join('/');
return {owner, repo};
}

/**
* authenticate the github using access token
* @param {String} accessToken the access token of copilot
Expand Down Expand Up @@ -79,14 +92,14 @@ async function _getUsernameById(github, id) {
/**
* updates the title of github issue
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {string} title new title
*/
async function updateIssue(copilot, repo, number, title) {
Joi.attempt({copilot, repo, number, title}, updateIssue.schema);
async function updateIssue(copilot, repoFullName, number, title) {
Joi.attempt({copilot, repoFullName, number, title}, updateIssue.schema);
const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({owner, repo, number, title});
} catch (err) {
Expand All @@ -97,22 +110,22 @@ async function updateIssue(copilot, repo, number, title) {

updateIssue.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
title: Joi.string().required()
};

/**
* Assigns the issue to user
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {string} user the user login of assignee
*/
async function assignUser(copilot, repo, number, user) {
Joi.attempt({copilot, repo, number, user}, assignUser.schema);
async function assignUser(copilot, repoFullName, number, user) {
Joi.attempt({copilot, repoFullName, number, user}, assignUser.schema);
const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
const issue = await github.issues.get({owner, repo, number});

Expand All @@ -129,23 +142,23 @@ async function assignUser(copilot, repo, number, user) {

assignUser.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
user: Joi.string().required()
};

/**
* Removes an assignee from the issue
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {string} user the user login of assignee
*/
async function removeAssign(copilot, repo, number, user) {
Joi.attempt({copilot, repo, number, user}, removeAssign.schema);
async function removeAssign(copilot, repoFullName, number, user) {
Joi.attempt({copilot, repoFullName, number, user}, removeAssign.schema);

const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
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 @@ -155,15 +168,15 @@ removeAssign.schema = assignUser.schema;
/**
* creates the comments on github issue
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {string} body the comment body text
*/
async function createComment(copilot, repo, number, body) {
Joi.attempt({copilot, repo, number, body}, createComment.schema);
async function createComment(copilot, repoFullName, number, body) {
Joi.attempt({copilot, repoFullName, number, body}, createComment.schema);

const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.createComment({owner, repo, number, body});
} catch (err) {
Expand All @@ -174,7 +187,7 @@ async function createComment(copilot, repo, number, body) {

createComment.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
body: Joi.string().required()
};
Expand Down Expand Up @@ -218,14 +231,14 @@ getUserIdByLogin.schema = {
/**
* updates the github issue as paid and fix accepted
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {Number} challengeId the challenge id
*/
async function markIssueAsPaid(copilot, repo, number, challengeId) {
Joi.attempt({copilot, repo, number, challengeId}, markIssueAsPaid.schema);
async function markIssueAsPaid(copilot, repoFullName, number, challengeId) {
Joi.attempt({copilot, repoFullName, number, challengeId}, markIssueAsPaid.schema);
const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
const labels = [config.PAID_ISSUE_LABEL, config.FIX_ACCEPTED_ISSUE_LABEL];
try {
await github.issues.edit({owner, repo, number, labels});
Expand All @@ -239,22 +252,22 @@ async function markIssueAsPaid(copilot, repo, number, challengeId) {

markIssueAsPaid.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
challengeId: Joi.number().positive().required()
};

/**
* change the state of github issue
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {string} state new state
*/
async function changeState(copilot, repo, number, state) {
Joi.attempt({copilot, repo, number, state}, changeState.schema);
async function changeState(copilot, repoFullName, number, state) {
Joi.attempt({copilot, repoFullName, number, state}, changeState.schema);
const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({owner, repo, number, state});
} catch (err) {
Expand All @@ -265,22 +278,22 @@ async function changeState(copilot, repo, number, state) {

changeState.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
state: Joi.string().required()
};

/**
* updates the github issue with new labels
* @param {Object} copilot the copilot
* @param {string} repo the repository
* @param {string} repoFullName the repository
* @param {Number} number the issue number
* @param {Number} labels the challenge id
*/
async function addLabels(copilot, repo, number, labels) {
Joi.attempt({copilot, repo, number, labels}, addLabels.schema);
async function addLabels(copilot, repoFullName, number, labels) {
Joi.attempt({copilot, repoFullName, number, labels}, addLabels.schema);
const github = await _authenticate(copilot.accessToken);
const owner = await _getUsernameById(github, copilot.userProviderId);
const {owner, repo} = _parseRepoUrl(repoFullName);
try {
await github.issues.edit({owner, repo, number, labels});
} catch (err) {
Expand All @@ -291,7 +304,7 @@ async function addLabels(copilot, repo, number, labels) {

addLabels.schema = {
copilot: copilotUserSchema,
repo: Joi.string().required(),
repoFullName: Joi.string().required(),
number: Joi.number().required(),
labels: Joi.array().items(Joi.string()).required()
};
Expand Down
54 changes: 28 additions & 26 deletions services/IssueService.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async function handleEventGracefully(event, issue, err) {
// reschedule event
if (event.retryCount <= config.RETRY_COUNT) {
logger.debug('Scheduling event for next retry');
const newEvent = { ...event };
const newEvent = {...event};
newEvent.retryCount += 1;
delete newEvent.copilot;
setTimeout(async () => {
Expand All @@ -74,27 +74,29 @@ async function handleEventGracefully(event, issue, err) {
logger.debug('The event is scheduled for retry');
}, config.RETRY_INTERVAL);
}
let comment = `[${err.statusCode}]: ${err.message}`;
if (event.event === 'issue.closed' && event.paymentSuccessful === false) {
comment = `Payment failed: ${comment}`;
}

if (event.retryCount === config.RETRY_COUNT) {
let comment = `[${err.statusCode}]: ${err.message}`;
if (event.event === 'issue.closed' && event.paymentSuccessful === false) {
comment = `Payment failed: ${comment}`;
}
// notify error in git host
if (event.provider === 'github') {
await gitHubService.createComment(event.copilot, event.data.repository.name, issue.number, comment);
await gitHubService.createComment(event.copilot, event.data.repository.full_name, issue.number, comment);
} else {
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
}
}
if (event.event === 'issue.closed') {
// reopen
await reOpenIssue(event, issue);
// ensure label is ready for review
const readyForReviewLabels = [config.READY_FOR_REVIEW_ISSUE_LABEL];
if (event.provider === 'github') {
await gitHubService.addLabels(event.copilot, event.data.repository.name, issue.number, readyForReviewLabels);
} else {
await gitlabService.addLabels(event.copilot, event.data.repository.id, issue.number, readyForReviewLabels);

if (event.event === 'issue.closed') {
// reopen
await reOpenIssue(event, issue);
// ensure label is ready for review
const readyForReviewLabels = [config.READY_FOR_REVIEW_ISSUE_LABEL];
if (event.provider === 'github') {
await gitHubService.addLabels(event.copilot, event.data.repository.full_name, issue.number, readyForReviewLabels);
} else {
await gitlabService.addLabels(event.copilot, event.data.repository.id, issue.number, readyForReviewLabels);
}
}
}
}
Expand All @@ -103,6 +105,7 @@ async function handleEventGracefully(event, issue, err) {

/**
* check if challenge is exists for given issue in db/topcoder
* @param {Object} event the event
* @param {Object} issue the issue
* @returns {Object} the found db issue if exists
* @private
Expand Down Expand Up @@ -175,7 +178,7 @@ async function assignUserAsRegistrant(topcoderUserId, challengeId) {
*/
async function reOpenIssue(event, issue) {
if (event.provider === 'github') {
await gitHubService.changeState(event.copilot, event.data.repository.name, issue.number, 'open');
await gitHubService.changeState(event.copilot, event.data.repository.full_name, issue.number, 'open');
} else {
await gitlabService.changeState(event.copilot, event.data.repository.id, issue.number, 'reopen');
}
Expand All @@ -200,9 +203,9 @@ async function rollbackAssignee(event, assigneeUserId, issue, reOpen = false) {
// comment on the git ticket for the user to self-sign up with the Topcoder x Self-Service tool
const comment = `@${assigneeUsername}, please sign-up with Topcoder X tool`;
if (event.provider === 'github') {
await gitHubService.createComment(event.copilot, event.data.repository.name, issue.number, comment);
await gitHubService.createComment(event.copilot, event.data.repository.full_name, issue.number, comment);
// un-assign the user from the ticket
await gitHubService.removeAssign(event.copilot, event.data.repository.name, issue.number, assigneeUsername);
await gitHubService.removeAssign(event.copilot, event.data.repository.full_name, issue.number, assigneeUsername);
} else {
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
// un-assign the user from the ticket
Expand Down Expand Up @@ -283,7 +286,7 @@ async function handleIssueAssignment(event, issue) {
const contestUrl = getUrlForChallengeId(dbIssue.challengeId);
const comment = `Contest ${contestUrl} has been updated - it has been assigned to ${userMapping.topcoderUsername}.`;
if (event.provider === 'github') {
await gitHubService.createComment(event.copilot, event.data.repository.name, issue.number, comment);
await gitHubService.createComment(event.copilot, event.data.repository.full_name, issue.number, comment);
} else {
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
}
Expand Down Expand Up @@ -312,15 +315,15 @@ async function handleIssueComment(event, issue) {
logger.debug(`updating issue: ${event.data.repository.name}/${issue.number}`);

if (event.provider === 'github') {
await gitHubService.updateIssue(event.copilot, event.data.repository.name, issue.number, newTitle);
await gitHubService.updateIssue(event.copilot, event.data.repository.full_name, issue.number, newTitle);
} else {
await gitlabService.updateIssue(event.copilot, event.data.repository.id, issue.number, newTitle);
}

// assign user
logger.debug(`assigning user, ${parsedComment.assignedUser} to issue: ${event.data.repository.name}/${issue.number}`);
if (event.provider === 'github') {
await gitHubService.assignUser(event.copilot, event.data.repository.name, issue.number, parsedComment.assignedUser);
await gitHubService.assignUser(event.copilot, event.data.repository.full_name, issue.number, parsedComment.assignedUser);
} else {
const userId = await gitlabService.getUserIdByLogin(event.copilot, parsedComment.assignedUser);
await gitlabService.assignUser(event.copilot, event.data.repository.id, issue.number, userId);
Expand Down Expand Up @@ -368,7 +371,7 @@ async function handleIssueUpdate(event, issue) {
const contestUrl = getUrlForChallengeId(dbIssue.challengeId);
const comment = `Contest ${contestUrl} has been updated - the new changes has been updated for this ticket.`;
if (event.provider === 'github') {
await gitHubService.createComment(event.copilot, event.data.repository.name, issue.number, comment);
await gitHubService.createComment(event.copilot, event.data.repository.full_name, issue.number, comment);
} else {
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
}
Expand Down Expand Up @@ -459,7 +462,7 @@ async function handleIssueClose(event, issue) {
try {
logger.debug('update issue as paid');
if (event.provider === 'github') {
await gitHubService.markIssueAsPaid(event.copilot, event.data.repository.name, issue.number, dbIssue.challengeId);
await gitHubService.markIssueAsPaid(event.copilot, event.data.repository.full_name, issue.number, dbIssue.challengeId);
} else {
await gitlabService.markIssueAsPaid(event.copilot, event.data.repository.id, issue.number, dbIssue.challengeId);
}
Expand Down Expand Up @@ -518,7 +521,7 @@ async function handleIssueCreate(event, issue) {
const contestUrl = getUrlForChallengeId(issue.challengeId);
const comment = `Contest ${contestUrl} has been created for this ticket.`;
if (event.provider === 'github') {
await gitHubService.createComment(event.copilot, event.data.repository.name, issue.number, comment);
await gitHubService.createComment(event.copilot, event.data.repository.full_name, issue.number, comment);
} else {
await gitlabService.createComment(event.copilot, event.data.repository.id, issue.number, comment);
}
Expand Down Expand Up @@ -618,7 +621,6 @@ async function process(event) {
issue.assignee = await gitlabService.getUsernameById(copilot, event.data.issue.assignees[0].id);
}
}
console.warn(JSON.stringify(issue));
if (event.event === 'issue.created') {
await handleIssueCreate(event, issue);
} else if (event.event === 'issue.updated') {
Expand Down