1
1
# Description:
2
- # Rally information for bugs, stories, and users
2
+ # Rally information for artifacts
3
3
#
4
4
# Dependencies:
5
5
# None
9
9
# HUBOT_RALLY_PASSWORD
10
10
#
11
11
# Commands:
12
- # hubot rally me <defect id | task id | story id > - Lookup a task, story or defect from Rally
12
+ # hubot rally me <formattedID > - Lookup a task, story, defect, etc. from Rally
13
13
#
14
14
# Notes:
15
15
# Since Rally supports rich text for description fields, it will come back as HTML
16
16
# to pretty print this we can run it through lynx. Make sure you have lynx installed
17
17
# and PATH accessible, otherwise we will degrade to just showing the html description.
18
- # ENV Variables Required:
19
18
#
20
19
# Author:
21
20
# brianmichel
@@ -24,107 +23,156 @@ exec = require('child_process').exec
24
23
25
24
user = process .env .HUBOT_RALLY_USERNAME
26
25
pass = process .env .HUBOT_RALLY_PASSWORD
27
- api_version = ' 1.40'
26
+ api_version = ' v2.0'
27
+
28
+ logger = null
29
+
30
+ typeInfoByPrefix =
31
+ DE :
32
+ name : ' defect'
33
+ extraOutputFields : [
34
+ ' State'
35
+ ' ScheduleState'
36
+ ' Severity'
37
+ ]
38
+ DS :
39
+ name : ' defectsuite'
40
+ extraOutputFields : [
41
+ ' ScheduleState'
42
+ ]
43
+ F :
44
+ name : ' feature'
45
+ queryName : ' portfolioitem/feature'
46
+ linkName : ' portfolioitem/feature'
47
+ extraOutputFields : [
48
+ ' State._refObjectName'
49
+ ' Parent._refObjectName'
50
+ ]
51
+ I :
52
+ name : ' initiative'
53
+ queryName : ' portfolioitem/initiative'
54
+ linkName : ' portfolioitem/initiative'
55
+ extraOutputFields : [
56
+ ' State._refObjectName'
57
+ ' Parent._refObjectName'
58
+ ]
59
+ T :
60
+ name : ' theme'
61
+ queryName : ' portfolioitem/theme'
62
+ linkName : ' portfolioitem/theme'
63
+ extraOutputFields : [
64
+ ' State._refObjectName'
65
+ ' Parent._refObjectName'
66
+ ]
67
+ TA :
68
+ name : ' task'
69
+ extraOutputFields : [
70
+ ' State'
71
+ ' WorkProduct._refObjectName'
72
+ ]
73
+ TC :
74
+ name : ' testcase'
75
+ extraOutputFields : [
76
+ ' WorkProduct._refObjectName'
77
+ ' Type'
78
+ ]
79
+ US :
80
+ name : ' story'
81
+ queryName : ' hierarchicalrequirement'
82
+ linkName : ' userstory'
83
+ extraOutputFields : [
84
+ ' ScheduleState'
85
+ ' Parent._refObjectName'
86
+ ' Feature._refObjectName'
87
+ ]
88
+
28
89
29
90
module .exports = (robot ) ->
30
- robot . respond / (rally)( me) ? ( . * ) / i , ( msg ) ->
31
- if user && pass
32
- switch msg . match [ 3 ]. toUpperCase (). substring ( 0 , 1 )
33
- when " D " then bugRequest msg, msg .match [3 ], ( string ) ->
34
- msg .send string
35
- when " T " then taskRequest msg, msg . match [ 3 ], ( string ) ->
36
- msg . send string
37
- when " S " then storyRequest msg, msg .match [ 3 ], ( string ) ->
38
- msg . send string
39
- else msg .send " Uhh, that doesn 't work "
40
- else
41
- msg .send " You need to set HUBOT_RALLY_USERNAME & HUBOT_RALLY_PASSWORD before making requests!"
91
+ logger = robot . logger
92
+ robot . respond / (rally)( me) ? ( [ a-z ] + )( \d + ) / i , ( msg ) ->
93
+ if user && pass
94
+ idPrefix = msg .match [3 ]. toUpperCase ()
95
+ idNumber = msg .match [ 4 ]
96
+ if typeInfoByPrefix . hasOwnProperty (idPrefix)
97
+ queryRequest msg, typeInfoByPrefix[idPrefix], idNumber, ( string ) ->
98
+ msg .send string
99
+ else
100
+ msg .send " Uhh, I don 't know that formatted ID prefix "
101
+ else
102
+ msg .send ' You need to set HUBOT_RALLY_USERNAME & HUBOT_RALLY_PASSWORD before making requests!'
42
103
43
- bugRequest = (msg , defectId , cb ) ->
44
- query_string = " /defect.js?query=(FormattedID = #{ defectId} )&fetch=true"
45
- rallyRequest msg, query_string, (json ) ->
46
- if json && json .QueryResult .TotalResultCount > 0
47
- getLinkToItem msg, json .QueryResult .Results [0 ], " defect"
48
- description = " No Description"
49
- if json .QueryResult .Results [0 ].Description
50
- prettifyDescription json .QueryResult .Results [0 ].Description , (output ) ->
51
- description = output
52
- return_array = [
53
- " #{ json .QueryResult .Results [0 ].FormattedID } - #{ json .QueryResult .Results [0 ]._refObjectName } "
54
- " Owner - #{ if json .QueryResult .Results [0 ].Owner then json .QueryResult .Results [0 ].Owner ._refObjectName else " No Owner" } "
55
- " Project - #{ if json .QueryResult .Results [0 ].Project then json .QueryResult .Results [0 ].Project ._refObjectName else " No Project" } "
56
- " Severity - #{ json .QueryResult .Results [0 ].Severity } "
57
- " State - #{ json .QueryResult .Results [0 ].State } "
58
- " #{ description} "
59
- ]
60
- cb return_array .join (" \n " )
61
- else
62
- cb " Aww snap, I couldn't find that bug!"
104
+ queryRequest = (msg , typeInfo , idNumber , cb ) ->
105
+ queryName = typeInfo .queryName || typeInfo .name
106
+ queryString = " /#{ queryName} .js?query=(FormattedID = #{ idNumber} )&fetch=true"
107
+ rallyRequest msg, queryString, (json ) ->
108
+ if json && json .QueryResult .TotalResultCount > 0
109
+ result = json .QueryResult .Results [0 ]
110
+ linkName = typeInfo .linkName || typeInfo .name
111
+ getLinkToItem msg, result, linkName
112
+ description = ' No Description'
113
+ prettifyDescription result .Description , (output ) ->
114
+ description = output || description
115
+ returnArray = [
116
+ " #{ result .FormattedID } - #{ result .Name } "
117
+ labeledField (result, ' Owner._refObjectName' )
118
+ labeledField (result, ' Project._refObjectName' )
119
+ ]
120
+ returnArray .push (labeledField (result, field)) for field in typeInfo .extraOutputFields
121
+ returnArray .push (" Description:" )
122
+ returnArray .push (" #{ description} " )
123
+ cb returnArray .join (" \n " )
124
+ else
125
+ cb " Aww snap, I couldn't find that #{ typeInfo .name } !"
63
126
64
- taskRequest = (msg , taskId , cb ) ->
65
- query_string = " /task.js?query=(FormattedID = #{ taskId} )&fetch=true"
66
- rallyRequest msg, query_string, (json ) ->
67
- if json && json .QueryResult .TotalResultCount > 0
68
- getLinkToItem msg, json .QueryResult .Results [0 ], " task"
69
- return_array = [
70
- " #{ json .QueryResult .Results [0 ].FormattedID } - #{ json .QueryResult .Results [0 ].Name } "
71
- " Owner - #{ if json .QueryResult .Results [0 ].Owner then json .QueryResult .Results [0 ].Owner ._refObjectName else " No Owner" } "
72
- " Project - #{ if json .QueryResult .Results [0 ].Project then json .QueryResult .Results [0 ].Project ._refObjectName else " No Project" } "
73
- " State - #{ json .QueryResult .Results [0 ].State } "
74
- " Feature - #{ if json .QueryResult .Results [0 ].WorkProduct ? json .QueryResult .Results [0 ].WorkProduct ._refObjectName else " Not associated with any feature" } "
75
- ]
76
- cb return_array .join (" \n " )
77
- else
78
- cb " Aww snap, I couldn't find that task!"
127
+ labeledField = (result , field ) ->
128
+ match = field .match (/ ^ (\w + )\. _refObjectName$ / )
129
+ if match
130
+ " #{ match[1 ]} : #{ refObjectName (result, match[1 ])} "
131
+ else
132
+ " #{ field} : #{ result[field]} "
79
133
80
- storyRequest = (msg , storyId , cb ) ->
81
- query_string = " /hierarchicalrequirement.js?query=(FormattedID = #{ storyId} )&fetch=true"
82
- rallyRequest msg, query_string, (json ) ->
83
- if json && json .QueryResult .TotalResultCount > 0
84
- getLinkToItem msg, json .QueryResult .Results [0 ], " userstory"
85
- description = " No Description"
86
- prettifyDescription json .QueryResult .Results [0 ].Description , (output ) ->
87
- description = output || description
88
- return_array = [
89
- " #{ json .QueryResult .Results [0 ].FormattedID } - #{ json .QueryResult .Results [0 ].Name } "
90
- " Owner - #{ if json .QueryResult .Results [0 ].Owner then json .QueryResult .Results [0 ].Owner ._refObjectName else " No Owner" } "
91
- " Project - #{ if json .QueryResult .Results [0 ].Project then json .QueryResult .Results [0 ].Project ._refObjectName else " No Project" } "
92
- " ScheduleState - #{ json .QueryResult .Results [0 ].ScheduleState } "
93
- " #{ description} "
94
- ]
95
- cb return_array .join (" \n " )
96
- else
97
- cb " Aww snap, I couldn't find that story!"
134
+ refObjectName = (result , field ) ->
135
+ if result[field] then result[field]._refObjectName else " No #{ field} "
98
136
99
137
rallyRequest = (msg , query , cb ) ->
100
- rally_url = ' https://rally1.rallydev.com/slm/webservice/' + api_version + query
101
- basicAuthRequest msg, rally_url, (json ) ->
102
- cb json
138
+ rally_url = ' https://rally1.rallydev.com/slm/webservice/' + api_version + query
139
+ # logger.debug "rally_url = #{rally_url}"
140
+ basicAuthRequest msg, rally_url, (json ) ->
141
+ # if json
142
+ # logger.debug "json = #{JSON.stringify(json)}"
143
+ cb json
103
144
104
145
basicAuthRequest = (msg , url , cb ) ->
105
- auth = ' Basic ' + new Buffer (user + ' :' + pass).toString (' base64' );
106
- msg .http (url)
107
- .headers (Authorization : auth, Accept : ' application/json' )
108
- .get () (err , res , body ) ->
109
- json_body = null
110
- switch res .statusCode
111
- when 200 then json_body = JSON .parse (body)
112
- else json_body = null
113
- cb json_body
146
+ auth = ' Basic ' + new Buffer (user + ' :' + pass).toString (' base64' );
147
+ msg .http (url)
148
+ .headers (Authorization : auth, Accept : ' application/json' )
149
+ .get () (err , res , body ) ->
150
+ json_body = null
151
+ switch res .statusCode
152
+ when 200 then json_body = JSON .parse (body)
153
+ else json_body = null
154
+ cb json_body
114
155
115
156
getLinkToItem = (msg , object , type ) ->
116
- project = if object && object .Project then object .Project else null
117
- if project
118
- objectId = object .ObjectID
119
- jsPos = project ._ref .lastIndexOf " .js"
120
- lastSlashPos = project ._ref .lastIndexOf " /"
121
- projectId = project ._ref [(lastSlashPos+ 1 ).. (jsPos- 1 )]
122
- msg .send " https://rally1.rallydev.com/slm/rally.sp#/#{ projectId} /detail/#{ type} /#{ objectId} "
123
- else
124
- # do nothing
157
+ project = if object && object .Project then object .Project else null
158
+ if project
159
+ objectId = object .ObjectID
160
+ jsPos = project ._ref .lastIndexOf ' .js'
161
+ lastSlashPos = project ._ref .lastIndexOf ' /'
162
+ projectId = project ._ref [(lastSlashPos+ 1 ).. (jsPos- 1 )]
163
+ msg .send " https://rally1.rallydev.com/#/#{ projectId} /detail/#{ type} /#{ objectId} "
164
+ else
165
+ # do nothing
166
+
125
167
prettifyDescription = (html_description , cb ) ->
126
- child = exec " echo \" #{ html_description} \" | lynx -dump -stdin" , (error , stdout , stderr ) ->
127
- return_text = html_description
128
- if ! error
129
- return_text = stdout
130
- cb return_text
168
+ child = exec " echo \" #{ html_description} \" | lynx -dump -stdin" , (error , stdout , stderr ) ->
169
+ return_text = html_description
170
+ if ! error
171
+ return_text = stdout
172
+ cb return_text
173
+
174
+ stripHtml = (html , cb ) ->
175
+ return_text = html .replace (/ <style. + \/ style>/ g , ' ' )
176
+ return_text = return_text .replace (/ <\/ ? . +? >/ g , ' ' ).replace (/ <br ? \/ ? >/ g , " \n\n " ).replace (/ / g , ' ' ).replace (/ [ ] + / g , ' ' ).replace (/ %22/ g , ' "' ).replace (/ &/ g , ' &' )
177
+ return_text = return_text .replace (/ >/ g , ' >' ).replace (/ </ g , ' <' )
178
+ cb return_text
0 commit comments