Created
June 15, 2017 03:03
-
-
Save mLuby/4e3442308cd27f9f669ba18639562da2 to your computer and use it in GitHub Desktop.
Determines average time for zenhub issue to be prioritized.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict" | |
// CONFIG | |
const ZENHUB_TOKEN = "GET IT FROM ZENHUB" | |
const GITHUB_TOKEN= "GET IT FROM GITHUB" | |
const ORG_NAME = "GITHUB ORG NAME" | |
const REPO_NAME = "REPO NAME" | |
// IMPORTS | |
const req = require("request-promise-native") | |
const moment = require("moment") | |
// INIT | |
getRepoId() | |
.then(log) | |
.then(getPipelinesWithIssues) | |
.then(log) | |
.then(pipelinesToNonNewIssueNumbers) | |
.then(log) | |
.then(getEventsForIssueNumbers) | |
.then(log) | |
.then(selectTransfersFromNewIssues) | |
.then(log) | |
.then(getPrioritizations) | |
.then(log) | |
.then(prioritizationStats) | |
.then(log) | |
.catch(console.error) | |
// FUNCTIONS | |
function getRepoId () { | |
return req({ | |
uri: `https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}`, | |
headers: { Authorization: `token ${GITHUB_TOKEN}`, "User-Agent": "myapp" }, json: true | |
}).then(repo => repo.id) | |
} | |
function getCollaboratorsById () { | |
return req({ | |
uri: `https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}/collaborators`, | |
headers: { Authorization: `token ${GITHUB_TOKEN}`, "User-Agent": "myapp" }, json: true | |
}).then(collaborators => Promise.all(collaborators.map(collaborator => req({ | |
uri: collaborator.url, | |
headers: { Authorization: `token ${GITHUB_TOKEN}`, "User-Agent": "myapp" }, json: true | |
})))) | |
.then(users => users.reduce((result, user) => (result[user.id] = `${user.name} <${user.login}>`, result), {})) | |
} | |
function getPipelinesWithIssues (repoId) { | |
return req({ | |
uri: `https://api.zenhub.io/p1/repositories/${repoId}/board`, | |
headers: {'X-Authentication-Token': ZENHUB_TOKEN}, json: true | |
}).then(board => board.pipelines) | |
} | |
function pipelinesToNonNewIssueNumbers (pipelines) { | |
return pipelines.filter(pipeline => pipeline.name !== "New Issues") | |
.map(pipeline => pipeline.issues) | |
.map(issues => issues | |
.filter(issue => !issue.is_epic) | |
.map(issue => issue.issue_number) | |
) | |
.reduce(flatten) | |
} | |
function getEventsForIssueNumbers (issueNumbers) { | |
console.log(`Getting events for ${issueNumbers.length} issueNumbers…`) | |
return new Promise((resolve, reject) => { | |
// issue all requests | |
const successfulResults = [] | |
const failedIssueNumbers = [] | |
let retryAllowedTime = null | |
return getRepoId().then(repoId => { | |
issueNumbers.forEach(issueNumber => { | |
// TODO should get github issue data in parallel here… | |
return req({ | |
uri: `https://api.zenhub.io/p1/repositories/${repoId}/issues/${issueNumber}/events`, | |
headers: {'X-Authentication-Token': ZENHUB_TOKEN}, json: true | |
}) | |
.then(events => { | |
const eventsWithIssueNumber = events.map(event => Object.assign(event, {issueNumber})) | |
successfulResults.push(eventsWithIssueNumber) | |
retryAllowedTime = lastly(successfulResults, failedIssueNumbers, resolve, retryAllowedTime) // returns existing retryAllowedTime | |
}) | |
.catch(error => { | |
failedIssueNumbers.push(issueNumber) | |
retryAllowedTime = lastly(successfulResults, failedIssueNumbers, resolve, retryAllowedTime, error) // returns latest wait time | |
}) | |
}) | |
function lastly (successfulResults, failedIssueNumbers, resolve, retryAllowedTime, error) { | |
const tmp = retryAllowedTime | |
retryAllowedTime = error && !isNaN(error.response.headers["x-ratelimit-reset"]) ? Number(error.response.headers["x-ratelimit-reset"]) * 1000 : retryAllowedTime // s -> ms | |
if (successfulResults.length + failedIssueNumbers.length === issueNumbers.length ) { | |
// when all requests have ended | |
if (successfulResults.length === issueNumbers.length) { | |
// if no failures, resolve with results | |
resolve(successfulResults) | |
} else { | |
// for failures | |
// figure out when to retry | |
const retryWaitMs = retryAllowedTime - Date.now() | |
// schedule retry and resolve successfulResults and results of the retry | |
console.log(`Waiting for ${retryWaitMs}ms (${moment.duration(retryWaitMs).humanize()}) to retry ${failedIssueNumbers.length} failedIssueNumbers… ${retryAllowedTime}`) | |
setTimeout(() => { | |
// console.log(`Wait elapsed! Retrying ${failedIssueNumbers.length} failedIssueNumbers…`) | |
getEventsForIssueNumbers(failedIssueNumbers) | |
.then(retriedSuccessfulResults => { | |
console.log("retriedSuccessfulResults", retriedSuccessfulResults) | |
const combinedResults = successfulResults.concat(retriedSuccessfulResults) | |
const filteredFlattenedResults = combinedResults.filter(a => a.length).reduce(flatten) | |
resolve(filteredFlattenedResults)//successfulResults.filter(a => a.length).concat(retriedSuccessfulResults.filter(a => a.length))) | |
}).catch(console.error) | |
}, retryWaitMs) | |
} | |
} | |
} | |
}) | |
}) | |
} | |
function selectTransfersFromNewIssues (events) { | |
return events.filter(isTransferFromNew) | |
} | |
function getPrioritizations (events) { | |
return getCollaboratorsById().then(githubIdToName => { | |
return Promise.all(events.map(event => { | |
return req({ | |
uri: `https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}/issues/${event.issueNumber}`, | |
headers: { | |
Authorization: `token ${GITHUB_TOKEN}`, | |
"User-Agent": "myapp" | |
}, | |
json: true // Automatically parses the JSON string in the response | |
}).then(issue => ({ | |
id: issue.number, | |
title: issue.title, | |
created_by: githubIdToName[issue.user.id] || issue.user.id, | |
created_at: issue.created_at, | |
prioritized_by: githubIdToName[event.user_id] || event.user_id, | |
prioritized_at: event.created_at, | |
prioritization_ms: (new Date(event.created_at)) - (new Date(issue.created_at)), | |
prioritization_human: moment.duration((new Date(event.created_at)) - (new Date(issue.created_at))).humanize() | |
})) | |
})).then(prioritizations => prioritizations.sort((a,b) => b.prioritization_ms - a.prioritization_ms)) | |
}) | |
} | |
function prioritizationStats (prioritizations) { | |
return { | |
totalPrioritizations: prioritizations.length, | |
averagePrioritization: moment.duration(prioritizations.map(p => p.prioritization_ms).reduce(sum)/prioritizations.length).humanize(), | |
averagePrioritizationMS: prioritizations.map(p => p.prioritization_ms).reduce(sum)/prioritizations.length | |
} | |
} | |
function log (x) { | |
console.log(x); return x | |
} | |
function flatten (list, item) { | |
return list.concat(item) | |
} | |
function isTransferFromNew (event) { | |
return event.type === "transferIssue" && event.from_pipeline.name === "New Issues" | |
} | |
function sum (sum, value) { | |
return sum + value | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment