Permalink
Fetching contributors…
Cannot retrieve contributors at this time
183 lines (167 sloc) 5.36 KB
# Description:
# Rally information for artifacts
#
# Dependencies:
# None
#
# Configuration:
# HUBOT_RALLY_USERNAME
# HUBOT_RALLY_PASSWORD
#
# Commands:
# hubot rally me <formattedID> - Lookup a task, story, defect, etc. from Rally
#
# Notes:
# Since Rally supports rich text for description fields, it will come back as HTML
# to pretty print this we can run it through lynx. Make sure you have lynx installed
# and PATH accessible, otherwise we will degrade to just showing the html description.
#
# Author:
# brianmichel
exec = require('child_process').exec
user = process.env.HUBOT_RALLY_USERNAME
pass = process.env.HUBOT_RALLY_PASSWORD
api_version = 'v2.0'
logger = null
typeInfoByPrefix =
DE:
name: 'defect'
extraOutputFields: [
'State'
'ScheduleState'
'Severity'
]
DS:
name: 'defectsuite'
extraOutputFields: [
'ScheduleState'
]
F:
name: 'feature'
queryName: 'portfolioitem/feature'
linkName: 'portfolioitem/feature'
extraOutputFields: [
'State._refObjectName'
'Parent._refObjectName'
]
I:
name: 'initiative'
queryName: 'portfolioitem/initiative'
linkName: 'portfolioitem/initiative'
extraOutputFields: [
'State._refObjectName'
'Parent._refObjectName'
]
T:
name: 'theme'
queryName: 'portfolioitem/theme'
linkName: 'portfolioitem/theme'
extraOutputFields: [
'State._refObjectName'
'Parent._refObjectName'
]
TA:
name: 'task'
extraOutputFields: [
'State'
'WorkProduct._refObjectName'
]
TC:
name: 'testcase'
extraOutputFields: [
'WorkProduct._refObjectName'
'Type'
]
US:
name: 'story'
queryName: 'hierarchicalrequirement'
linkName: 'userstory'
extraOutputFields: [
'ScheduleState'
'Parent._refObjectName'
'Feature._refObjectName'
]
module.exports = (robot) ->
logger = robot.logger
robot.respond /(rally)( me)? ([a-z]+)(\d+)/i, (msg) ->
if user && pass
idPrefix = msg.match[3].toUpperCase()
idNumber = msg.match[4]
if typeInfoByPrefix.hasOwnProperty(idPrefix)
queryRequest msg, typeInfoByPrefix[idPrefix], idNumber, (string) ->
msg.send string
else
msg.send "Uhh, I don't know that formatted ID prefix"
else
msg.send 'You need to set HUBOT_RALLY_USERNAME & HUBOT_RALLY_PASSWORD before making requests!'
queryRequest = (msg, typeInfo, idNumber, cb) ->
queryName = typeInfo.queryName || typeInfo.name
queryString = "/#{queryName}.js?query=(FormattedID = #{idNumber})&fetch=true"
rallyRequest msg, queryString, (json) ->
if json && json.QueryResult.TotalResultCount > 0
result = json.QueryResult.Results[0]
linkName = typeInfo.linkName || typeInfo.name
getLinkToItem msg, result, linkName
description = 'No Description'
prettifyDescription result.Description, (output) ->
description = output || description
returnArray = [
"#{result.FormattedID} - #{result.Name}"
labeledField(result, 'Owner._refObjectName')
labeledField(result, 'Project._refObjectName')
]
returnArray.push(labeledField(result, field)) for field in typeInfo.extraOutputFields
returnArray.push("Description:")
returnArray.push("#{description}")
cb returnArray.join("\n")
else
cb "Aww snap, I couldn't find that #{typeInfo.name}!"
labeledField = (result, field) ->
match = field.match(/^(\w+)\._refObjectName$/)
if match
"#{match[1]}: #{refObjectName(result, match[1])}"
else
"#{field}: #{result[field]}"
refObjectName = (result, field) ->
if result[field] then result[field]._refObjectName else "No #{field}"
rallyRequest = (msg, query, cb) ->
rally_url = 'https://rally1.rallydev.com/slm/webservice/' + api_version + query
# logger.debug "rally_url = #{rally_url}"
basicAuthRequest msg, rally_url, (json) ->
# if json
# logger.debug "json = #{JSON.stringify(json)}"
cb json
basicAuthRequest = (msg, url, cb) ->
auth = 'Basic ' + new Buffer(user + ':' + pass).toString('base64');
msg.http(url)
.headers(Authorization: auth, Accept: 'application/json')
.get() (err, res, body) ->
json_body = null
switch res.statusCode
when 200 then json_body = JSON.parse(body)
else json_body = null
cb json_body
getLinkToItem = (msg, object, type) ->
project = if object && object.Project then object.Project else null
if project
objectId = object.ObjectID
jsPos = project._ref.lastIndexOf '.js'
lastSlashPos = project._ref.lastIndexOf '/'
projectId = project._ref[(lastSlashPos+1)..(jsPos)]
msg.send "https://rally1.rallydev.com/#/#{projectId}/detail/#{type}/#{objectId}"
else
#do nothing
stripHtml = (html, cb) ->
return_text = html.replace(/<style.+\/style>/g, '')
return_text = return_text.replace(/<br ?\/?>/g, "\n\n").replace(/&nbsp;/g, ' ').replace(/[ ]+/g, ' ').replace(/%22/g, '"').replace(/&amp;/g, '&').replace(/<\/?.+?>/g, '')
return_text = return_text.replace(/&gt;/g, '>').replace(/&lt;/g, '<')
cb return_text
prettifyDescription = (html_description, cb) ->
child = exec "echo \"#{html_description}\" | lynx -dump -stdin", (error, stdout, stderr) ->
return_text = html_description
if !error
return_text = stdout
else
stripHtml return_text, (cleaned) ->
return_text = cleaned
cb return_text