Create a gist now

Instantly share code, notes, and snippets.

@ehues /wi.js
Created Mar 15, 2015

What would you like to do?
Script that finds all open work items in a hierarchy and records the subscriber and vote counts for each WI
var request = require('request');
var fs = require('fs');
var Promise = require('promise');
function WorkItems(repo, username, password) {
this.req = request.defaults({
jar: request.jar(),
followAllRedirects: true,
rejectUnauthorized: false
});
this.repo = repo;
this.username = username;
this.password = password;
}
WorkItems.prototype.fetchOSLC = function(workitemNumber, avoidLogin) {
var self = this;
return new Promise(function(resolve, reject) {
self.req.get(self.repo + "/oslc/workitems/" + workitemNumber + ".json",
function (err, resp, body) {
if (resp.statusCode == 401) {
// login and retry
if (avoidLogin) {
reject("Log in failure when fetching work item.");
return;
}
return self.login().then(function() {
console.log("after login");
self.fetchOSLC(workitemNumber, true).then(resolve, reject);
}, reject);
}
if (resp.statusCode == 200) {
resolve(JSON.parse(body));
}
else {
reject("Couldn't fetch work item. Got a " + resp.statusCode + " during request.");
}
}
);
});
};
WorkItems.prototype.login = function() {
var self = this;
return new Promise(function(resolve, reject) {
self.req.get(self.repo + "/authenticated/identity", function(err, resp, body) {
if (err) {
return reject(err);
}
if (resp.statusCode != 401) {
console.log("/auth/id");
return reject("Error during login. Unexpected status code " + resp.statusCode + ".");
}
self.req.post(self.repo + "/authenticated/j_security_check", {
form: {
j_username: self.username,
j_password: self.password
}
}, function(err, resp, body) {
if (resp.statusCode != 404 && resp.statusCode != 500) {
console.log("/auth/j_sec");
return reject("Unexpected status code after login " + resp.statusCode + ".");
}
resolve(self);
});
});
});
};
WorkItems.prototype.queryForCategories = function(paId, perCategoryCallback) {
var self = this;
return new Promise(function(resolve, reject) {
var uri = self.repo + "/oslc/categories?oslc_cm.query=";
uri += encodeURIComponent("rtc_cm:projectArea=\"" + paId + "\"");
console.log(uri);
var catsHandled = 0;
var resultGatherer;
resultGatherer = function(err, resp, body) {
if (err) {
return reject(err);
}
if (resp.statusCode != 200) {
console.log("status code " + resp.statusCode);
return reject("Error during login. Unexpected status code " + resp.statusCode + ".");
}
var answer = JSON.parse(body);
answer["oslc_cm:results"].forEach(perCategoryCallback);
catsHandled += answer["oslc_cm:results"].length;
console.log("Handled " + catsHandled + " of " + answer["oslc_cm:totalCount"]);
if (answer["oslc_cm:next"]) {
self.req.get(answer["oslc_cm:next"], {headers: {
'accept': 'application/json'
}}, resultGatherer);
}
else {
resolve();
}
};
self.req.get(uri, {headers: {
'accept': 'application/json'
}}, resultGatherer);
});
}
/**
* Fire the callback once for each workitem. Returns a promise that is resolved
* when all of the work items have been queried
*/
WorkItems.prototype.queryForWorkItems = function(paId, queryString, perWorkitemCallback, properties) {
var self = this;
return new Promise(function(resolve, reject) {
var uri = self.repo + "/oslc/contexts/" + paId + "/workitems?oslc_cm.query=";
uri += encodeURIComponent(queryString);
if (properties) {
uri += "&oslc_cm.properties=" + encodeURIComponent(properties.join());
}
var wisHandled = 0;
var resultGatherer;
resultGatherer = function(err, resp, body) {
if (err) {
return reject(err);
}
if (resp.statusCode != 200) {
console.log("status code " + resp.statusCode);
return reject("Error during login. Unexpected status code " + resp.statusCode + ".");
}
var answer = JSON.parse(body);
answer["oslc_cm:results"].forEach(perWorkitemCallback);
wisHandled += answer["oslc_cm:results"].length;
console.log("Handled " + wisHandled + " of " + answer["oslc_cm:totalCount"]);
if (answer["oslc_cm:next"]) {
self.req.get(answer["oslc_cm:next"], {headers: {
'accept': 'application/json'
}}, resultGatherer);
}
else {
resolve();
}
};
self.req.get(uri, {headers: {
'accept': 'application/json'
}}, resultGatherer);
});
}
var REPO = process.env.RTC_REPO;
var USER = process.env.RTC_USER;
var PASS = process.env.RTC_PASS;
var OUTFILE = "/tmp/wis.csv";
// Clear out our output file
try {
fs.unlinkSync(OUTFILE);
} catch (e) {
}
fs.appendFileSync(OUTFILE, "voters, subscribers, id, summary\n");
// Callback that gathers work items
function wiGatherer(wi) {
var subCount = 0;
var voteCount = 0;
if (wi['rtc_cm:com.ibm.team.workitem.attribute.voting.upvoters']) {
voteCount = wi['rtc_cm:com.ibm.team.workitem.attribute.voting.upvoters'].length;
}
// Write a wad of csv
fs.appendFileSync(OUTFILE, "" + voteCount + ", " + wi['rtc_cm:subscribers'].length + ", " + wi['dc:identifier']+ ", \"" + wi['dc:title'].replace(/"/g, '""') + "\"\n");
}
// Callback that gathers the categories whose name starts with Source Control
var cats = [];
function categoryGatherer(cat) {
var name = cat["rtc_cm:hierarchicalName"];
if (name.lastIndexOf("Source Control", 0) === 0) {
console.log("Got " + name);
cats.push(cat);
}
}
wi = new WorkItems(REPO, USER, PASS);
wi.login().then(function () {
// Find all the work item categories
return wi.queryForCategories("_1w8aQEmJEduIY7C8B09Hyw", categoryGatherer);
}).then(function () {
var everyCatPromise = [];
// Walk the work item categories and find the open WIs
cats.forEach(function (cat) {
var path = cat["rdf:resource"];
var catName = cat["rtc_cm:hierarchicalName"];
console.log("Checking " + catName);
var catProm = wi.queryForWorkItems("_1w8aQEmJEduIY7C8B09Hyw", "rtc_cm:state=\"{open}\" and rtc_cm:filedAgainst=\"" + path + "\"", wiGatherer, ['dc:title', 'dc:identifier', 'rtc_cm:subscribers', 'rtc_cm:com.ibm.team.workitem.attribute.voting.upvoters']);
everyCatPromise.push(catProm);
});
// vvv not necessary
return Promise.all(everyCatPromise);
});
module.exports = WorkItems;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment