Skip to content

Instantly share code, notes, and snippets.

@rayburgemeestre
Created November 14, 2014 12:24
Show Gist options
  • Save rayburgemeestre/eba6b928821f97c1b3e6 to your computer and use it in GitHub Desktop.
Save rayburgemeestre/eba6b928821f97c1b3e6 to your computer and use it in GitHub Desktop.
tampermonkey_agile_board_plan_blocked.js
// ==UserScript==
// @name Visualize blocked tickets in current sprint on Jira Agile board Plan board.
// @namespace http://cppse.nl
// @version 1.0
// @description It will fetch all `status != Done` issues in the current sprint with an API call,
// and see if there are "is blocked by" relations to tickets that are not yet `status = Done`.
// @author Ray Burgemeetre
// @match https://*.atlassian.net/secure/RapidBoard.jspa*view=planning*
// @grant none
// ==/UserScript==
(function () {
// Configuration
var jiraProject = 'AT'.replace(/'/g, "\\'"),
// The JQL for fetching the !Done tickets in sprint
jql = "project = '" + jiraProject + "' and sprint in openSprints() and status != 'Done' AND type not in (Sub-task) order by Rank",
// The JQL (pattern) for fetching specific !Done tickets
jql2 = "key IN (<<issues>>) and status != 'Done'";
function visualizeCurrentSprintBlockedIssuesInPlanMode(isBlockedBy, blocks)
{
var jql = escape(jql2.replace('<<issues>>', isBlockedBy.join(", ")));
$.ajax({
url : "//" + window.location.host + "/rest/api/latest/search?jql=" + jql,
type : "GET",
dataType : "json",
contentType : "application/json",
success : function(response)
{
var issues = {}, issueElems = document.querySelectorAll('.js-issue'), i, j,
blockedIssues = {};
// All found issues are not yet done, so they are blocking issues *now*
for (i in response['issues']) {
if (!response['issues'][i].key)
continue;
// Store the blockedIssue for easy lookup
for (j=0; j<blocks[response['issues'][i].key].length; j++) {
var blockedIssue = blocks[response['issues'][i].key][j];
blockedIssues[blockedIssue] = true;
}
}
// Highlight the blocked issue's element in plan mode
for (i=0; i<issueElems.length; i++) {
var key = issueElems[i].getAttribute('data-issue-key'), isSubTask, color = 'red';
if (blockedIssues[key])
issueElems[i].style.border = 'solid 3px red';
}
},
error : function(jqXHR, textStatus, errorThrown)
{
console.log(textStatus + " : " + errorThrown);
// Retry on failure
setTimeout(lookupCurrentSprintBlocksRelations, 2000);
}
});
}
function lookupCurrentSprintBlocksRelations()
{
$.ajax({
url : "//" + window.location.host + "/rest/api/latest/search?jql=" + escape(jql),
type : "GET",
dataType : "json",
contentType : "application/json",
success : function(response)
{
var issues = {}, links, i, j, isBlockedBy = [], blocks = {}, from, to;
// Create lookup object for easily checking if issues are in the set
for (i in response['issues']) {
issues[response['issues'][i].key] = true;
}
// Search all issues for block relations, and record the "is blocked by" relations.
for (i in response['issues']) {
if (!response['issues'][i]['fields'])
continue;
links = response['issues'][i]['fields']['issuelinks'];
if (!links.length)
continue;
for (j=0; j<links.length; j++) {
if (links[j].type.name !== 'Blocks')
continue;
if (links[j].inwardIssue) {
from = links[j].inwardIssue.key;
to = response['issues'][i]['key'];
if (!blocks[from])
blocks[from] = [];
blocks[from].push(to);
console.log(to + ' is blocked by ' + from);
// Necessary to look this one up..
if (!issues[from])
isBlockedBy.push(from);
}
else {
from = response['issues'][i]['key'];
to = links[j].outwardIssue.key;
if (!blocks[from])
blocks[from] = [];
blocks[from].push(to);
console.log(from + ' blocks ' + to);
}
}
}
visualizeCurrentSprintBlockedIssuesInPlanMode(isBlockedBy, blocks);
},
error : function(jqXHR, textStatus, errorThrown)
{
console.log(textStatus + " : " + errorThrown);
// Periodically *retry*
setTimeout(lookupCurrentSprintBlocksRelations, 2000);
}
});
}
window.onload = function ()
{
lookupCurrentSprintBlocksRelations();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment