diff --git a/services/GithubService.js b/services/GithubService.js index cfef8a2..89b05e0 100644 --- a/services/GithubService.js +++ b/services/GithubService.js @@ -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 @@ -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) { @@ -97,7 +110,7 @@ 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() }; @@ -105,14 +118,14 @@ updateIssue.schema = { /** * 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}); @@ -129,7 +142,7 @@ 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() }; @@ -137,15 +150,15 @@ assignUser.schema = { /** * 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}`); } @@ -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) { @@ -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() }; @@ -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}); @@ -239,7 +252,7 @@ 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() }; @@ -247,14 +260,14 @@ markIssueAsPaid.schema = { /** * 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) { @@ -265,7 +278,7 @@ 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() }; @@ -273,14 +286,14 @@ changeState.schema = { /** * 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) { @@ -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() }; diff --git a/services/IssueService.js b/services/IssueService.js index a802833..c45e67c 100755 --- a/services/IssueService.js +++ b/services/IssueService.js @@ -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 () => { @@ -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); + } } } } @@ -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 @@ -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'); } @@ -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 @@ -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); } @@ -312,7 +315,7 @@ 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); } @@ -320,7 +323,7 @@ async function handleIssueComment(event, issue) { // 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); @@ -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); } @@ -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); } @@ -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); } @@ -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') {