jiraSOAP 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +2 -5
- data/lib/jiraSOAP/JIRAservice.rb +12 -8
- data/lib/jiraSOAP/handsoap_extensions.rb +12 -12
- data/lib/jiraSOAP/macruby_stuff.rb +12 -1
- data/lib/jiraSOAP/remoteAPI.rb +390 -89
- data/lib/jiraSOAP/remoteEntities.rb +278 -29
- data/lib/jiraSOAP/url.rb +21 -0
- data/lib/jiraSOAP.rb +3 -1
- metadata +32 -5
data/README.markdown
CHANGED
@@ -46,11 +46,7 @@ Once that ugliness is over with, you can run a quick demo (making appropriate su
|
|
46
46
|
|
47
47
|
Get the [Gist](http://gist.github.com/612186).
|
48
48
|
|
49
|
-
|
50
|
-
Notes About Using This Gem
|
51
|
-
--------------------------
|
52
|
-
|
53
|
-
1. URESOLVED issues have a Resolution with a value of `nil`.
|
49
|
+
Read the documentation [here](http://rdoc.info/github/Marketcircle/jiraSOAP/master/frames). The meat of the service is in the `RemoteAPI` module.
|
54
50
|
|
55
51
|
|
56
52
|
TODO
|
@@ -58,6 +54,7 @@ TODO
|
|
58
54
|
|
59
55
|
- Performance optimizations; there are a number of places that can be optimized
|
60
56
|
+ Using GCD/Threads for parsing arrays of results; a significant speed up for large types and large arrays (ie. creating issues from JQL searches)
|
57
|
+
- Refactor for a smaller code base
|
61
58
|
- Fix type hacks;. dates should be `NSDate`s and URLs should be `NSURL`s, right now they are all strings
|
62
59
|
- Public test suite
|
63
60
|
+ Needs a mock server
|
data/lib/jiraSOAP/JIRAservice.rb
CHANGED
@@ -9,25 +9,29 @@ module JIRA
|
|
9
9
|
# will end up messing up any other instances currently being used.
|
10
10
|
#
|
11
11
|
# It is best to treat this class as a singleton. There should only be one.
|
12
|
+
# However, this is not enforced, in case you want to be able to login as
|
13
|
+
# multiple users to the same endpoint.
|
12
14
|
#
|
13
15
|
# HTTPS is not supported in this version.
|
16
|
+
#
|
17
|
+
# @todo consider adding a finalizer that will try to logout
|
14
18
|
class JIRAService < Handsoap::Service
|
15
19
|
include RemoteAPI
|
16
20
|
|
17
21
|
attr_reader :auth_token, :user
|
18
22
|
|
19
23
|
# Factory method to initialize and login.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def self.
|
24
|
+
# @param [String] url URL for the JIRA server
|
25
|
+
# @param [String] user JIRA user name to login with
|
26
|
+
# @param [String] password
|
27
|
+
def self.instance_with_endpoint(url, user, password)
|
24
28
|
jira = JIRAService.new url
|
25
29
|
jira.login user, password
|
26
30
|
jira
|
27
31
|
end
|
28
32
|
|
29
33
|
# Slightly hacky in order to set the endpoint at the initialization.
|
30
|
-
|
34
|
+
# @param endpoint_url URL for the JIRA server
|
31
35
|
def initialize(endpoint_url)
|
32
36
|
super
|
33
37
|
|
@@ -39,11 +43,11 @@ class JIRAService < Handsoap::Service
|
|
39
43
|
self.class.endpoint endpoint_data
|
40
44
|
end
|
41
45
|
|
42
|
-
#PONDER: a finalizer that will try to logout
|
43
|
-
|
44
46
|
# Something to help users out until the rest of the API is implemented.
|
45
47
|
def method_missing(method, *args)
|
46
|
-
message = 'Check the documentation; the method may not be implemented
|
48
|
+
message = 'Check the documentation; the method may not be implemented or '
|
49
|
+
message << 'has changed in recent revisions. The client side API has not '
|
50
|
+
message << 'been stabilized yet.'
|
47
51
|
raise NoMethodError, message, caller
|
48
52
|
end
|
49
53
|
|
@@ -1,24 +1,24 @@
|
|
1
|
-
#Some simple extensions to Handsoap.
|
1
|
+
# Some simple extensions to Handsoap.
|
2
2
|
module Handsoap
|
3
|
-
#Some simple extensions to XmlMason. Currently, this only includes methods to
|
4
|
-
#make it easier to build SOAP messages that contain arrays.
|
3
|
+
# Some simple extensions to XmlMason. Currently, this only includes methods to
|
4
|
+
# make it easier to build SOAP messages that contain arrays.
|
5
5
|
module XmlMason
|
6
|
-
#Represents a node in an XML document
|
6
|
+
# Represents a node in an XML document used for SOAP message creation.
|
7
7
|
class Node
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# @todo Make this method recursive
|
9
|
+
# @param [String] node_name
|
10
|
+
# @param [Array] array
|
11
|
+
# @param [Hash] options
|
12
12
|
def add_simple_array(node_name, array = [], options = {})
|
13
13
|
prefix, name = parse_ns(node_name)
|
14
14
|
node = append_child Element.new(self, prefix, name, nil, options)
|
15
15
|
array.each { |element| node.add node_name, element }
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
# @todo Make this method recursive
|
19
|
+
# @param [String] node_name
|
20
|
+
# @param [Array] array
|
21
|
+
# @param [Hash] options
|
22
22
|
def add_complex_array(node_name, array = [], options = {})
|
23
23
|
prefix, name = parse_ns(node_name)
|
24
24
|
node = append_child Element.new(self, prefix, name, nil, options)
|
@@ -1,4 +1,15 @@
|
|
1
|
-
|
1
|
+
framework 'Foundation'
|
2
|
+
|
3
|
+
# work around curb not building for MacRuby (ticket #941)
|
4
|
+
Handsoap.http_driver = :net_http
|
5
|
+
|
6
|
+
class URL
|
7
|
+
def initialize(url_string)
|
8
|
+
@url = NSURL.URLWithString url_string
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method absoluteString to_s
|
12
|
+
end
|
2
13
|
|
3
14
|
module JIRA
|
4
15
|
#overrides for MacRuby
|
data/lib/jiraSOAP/remoteAPI.rb
CHANGED
@@ -3,29 +3,29 @@
|
|
3
3
|
#@todo exception handling
|
4
4
|
#@todo code refactoring and de-duplication
|
5
5
|
module RemoteAPI
|
6
|
-
#XPath constant to get a node containing a response array.
|
7
|
-
#This could be used for all responses, but is only used in cases where we
|
8
|
-
#cannot use a more blunt XPath expression.
|
6
|
+
# XPath constant to get a node containing a response array.
|
7
|
+
# This could be used for all responses, but is only used in cases where we
|
8
|
+
# cannot use a more blunt XPath expression.
|
9
9
|
RESPONSE_XPATH = '/node()[1]/node()[1]/node()[1]/node()[2]'
|
10
10
|
|
11
|
-
#The first method to call
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# The first method to call; other methods will fail until you are logged in.
|
12
|
+
# @param [String] user JIRA user name to login with
|
13
|
+
# @param [String] password
|
14
|
+
# @return [true] true if successful, otherwise an exception is thrown
|
15
15
|
def login(user, password)
|
16
16
|
response = invoke('soap:login') { |msg|
|
17
17
|
msg.add 'soap:in0', user
|
18
18
|
msg.add 'soap:in1', password
|
19
19
|
}
|
20
|
-
#cache now that we know it is safe to do so
|
20
|
+
# cache now that we know it is safe to do so
|
21
21
|
@user = user
|
22
22
|
@auth_token = response.document.xpath('//loginReturn').first.to_s
|
23
23
|
true
|
24
24
|
end
|
25
25
|
|
26
|
-
#You only need to call this to make an
|
27
|
-
#
|
28
|
-
|
26
|
+
# You only need to call this to make an explicit logout; normally, a session
|
27
|
+
# will automatically expire after a set time (configured on the server).
|
28
|
+
# @return [true] true if successful, otherwise false
|
29
29
|
def logout
|
30
30
|
response = invoke('soap:logout') { |msg|
|
31
31
|
msg.add 'soap:in0', @auth_token
|
@@ -33,8 +33,7 @@ module RemoteAPI
|
|
33
33
|
response.document.xpath('//logoutReturn').first.to_s == 'true'
|
34
34
|
end
|
35
35
|
|
36
|
-
#
|
37
|
-
#@return [[JIRA::Priority]]
|
36
|
+
# @return [[JIRA::Priority]]
|
38
37
|
def get_priorities
|
39
38
|
response = invoke('soap:getPriorities') { |msg|
|
40
39
|
msg.add 'soap:in0', @auth_token
|
@@ -45,8 +44,7 @@ module RemoteAPI
|
|
45
44
|
}
|
46
45
|
end
|
47
46
|
|
48
|
-
#
|
49
|
-
#@return [[JIRA::Resolution]]
|
47
|
+
# @return [[JIRA::Resolution]]
|
50
48
|
def get_resolutions
|
51
49
|
response = invoke('soap:getResolutions') { |msg|
|
52
50
|
msg.add 'soap:in0', @auth_token
|
@@ -57,8 +55,7 @@ module RemoteAPI
|
|
57
55
|
}
|
58
56
|
end
|
59
57
|
|
60
|
-
#
|
61
|
-
#@return [[JIRA::Field]]
|
58
|
+
# @return [[JIRA::Field]]
|
62
59
|
def get_custom_fields
|
63
60
|
response = invoke('soap:getCustomFields') { |msg|
|
64
61
|
msg.add 'soap:in0', @auth_token
|
@@ -69,8 +66,7 @@ module RemoteAPI
|
|
69
66
|
}
|
70
67
|
end
|
71
68
|
|
72
|
-
#
|
73
|
-
#@return [[JIRA::IssueType]]
|
69
|
+
# @return [[JIRA::IssueType]]
|
74
70
|
def get_issue_types
|
75
71
|
response = invoke('soap:getIssueTypes') { |msg|
|
76
72
|
msg.add 'soap:in0', @auth_token
|
@@ -81,8 +77,7 @@ module RemoteAPI
|
|
81
77
|
}
|
82
78
|
end
|
83
79
|
|
84
|
-
#
|
85
|
-
#@return [[JIRA::Status]]
|
80
|
+
# @return [[JIRA::Status]]
|
86
81
|
def get_statuses
|
87
82
|
response = invoke('soap:getStatuses') { |msg|
|
88
83
|
msg.add 'soap:in0', @auth_token
|
@@ -93,8 +88,7 @@ module RemoteAPI
|
|
93
88
|
}
|
94
89
|
end
|
95
90
|
|
96
|
-
#
|
97
|
-
#@return [[JIRA::Scheme]]
|
91
|
+
# @return [[JIRA::Scheme]]
|
98
92
|
def get_notification_schemes
|
99
93
|
response = invoke('soap:getNotificationSchemes') { |msg|
|
100
94
|
msg.add 'soap:in0', @auth_token
|
@@ -105,9 +99,8 @@ module RemoteAPI
|
|
105
99
|
}
|
106
100
|
end
|
107
101
|
|
108
|
-
#
|
109
|
-
|
110
|
-
#@return [[JIRA::Version]]
|
102
|
+
# @param [String] project_key
|
103
|
+
# @return [[JIRA::Version]]
|
111
104
|
def get_versions_for_project(project_key)
|
112
105
|
response = invoke('soap:getVersions') { |msg|
|
113
106
|
msg.add 'soap:in0', @auth_token
|
@@ -119,9 +112,8 @@ module RemoteAPI
|
|
119
112
|
}
|
120
113
|
end
|
121
114
|
|
122
|
-
#
|
123
|
-
|
124
|
-
#@return [JIRA::Project]
|
115
|
+
# @param [String] project_key
|
116
|
+
# @return [JIRA::Project]
|
125
117
|
def get_project_with_key(project_key)
|
126
118
|
response = invoke('soap:getProjectByKey') { |msg|
|
127
119
|
msg.add 'soap:in0', @auth_token
|
@@ -131,9 +123,8 @@ module RemoteAPI
|
|
131
123
|
JIRA::Project.project_with_xml_fragment frag
|
132
124
|
end
|
133
125
|
|
134
|
-
#
|
135
|
-
|
136
|
-
#@return [JIRA::User]
|
126
|
+
# @param [String] user_name
|
127
|
+
# @return [JIRA::User]
|
137
128
|
def get_user_with_name(user_name)
|
138
129
|
response = invoke('soap:getUser') { |msg|
|
139
130
|
msg.add 'soap:in0', @auth_token
|
@@ -142,10 +133,10 @@ module RemoteAPI
|
|
142
133
|
JIRA::User.user_with_xml_fragment response.document.xpath '//getUserReturn'
|
143
134
|
end
|
144
135
|
|
145
|
-
#
|
146
|
-
#the avatars for a project, use {#get_project_avatars_for_key}.
|
147
|
-
|
148
|
-
|
136
|
+
# Gets you the default avatar image for a project; if you want all
|
137
|
+
# the avatars for a project, use {#get_project_avatars_for_key}.
|
138
|
+
# @param [String] project_key
|
139
|
+
# @return [JIRA::Avatar]
|
149
140
|
def get_project_avatar_for_key(project_key)
|
150
141
|
response = invoke('soap:getProjectAvatar') { |msg|
|
151
142
|
msg.add 'soap:in0', @auth_token
|
@@ -154,10 +145,10 @@ module RemoteAPI
|
|
154
145
|
JIRA::Avatar.avatar_with_xml_fragment response.document.xpath '//getProjectAvatarReturn'
|
155
146
|
end
|
156
147
|
|
157
|
-
#
|
158
|
-
#just want the default avatar, use {#get_project_avatar_for_key}.
|
159
|
-
|
160
|
-
|
148
|
+
# Gets ALL avatars for a given project use this method; if you
|
149
|
+
# just want the default avatar, use {#get_project_avatar_for_key}.
|
150
|
+
# @param [String] project_key
|
151
|
+
# @return [[JIRA::Avatar]]
|
161
152
|
def get_project_avatars_for_key(project_key)
|
162
153
|
response = invoke('soap:getProjectAvatars') { |msg|
|
163
154
|
msg.add 'soap:in0', @auth_token
|
@@ -169,12 +160,12 @@ module RemoteAPI
|
|
169
160
|
}
|
170
161
|
end
|
171
162
|
|
172
|
-
#This method is the equivalent of making an advanced search from the
|
173
|
-
#web interface.
|
174
|
-
|
175
|
-
|
163
|
+
# This method is the equivalent of making an advanced search from the
|
164
|
+
# web interface.
|
165
|
+
# @param [String] jql_query JQL query as a string
|
166
|
+
# @param [Fixnum] max_results limit on number of returned results;
|
176
167
|
# the value may be overridden by the server if max_results is too large
|
177
|
-
|
168
|
+
# @return [[JIRA::Issue]]
|
178
169
|
def get_issues_from_jql_search(jql_query, max_results = 500)
|
179
170
|
response = invoke('soap:getIssuesFromJqlSearch') { |msg|
|
180
171
|
msg.add 'soap:in0', @auth_token
|
@@ -187,29 +178,29 @@ module RemoteAPI
|
|
187
178
|
}
|
188
179
|
end
|
189
180
|
|
190
|
-
#This method can update most, but not all, issue fields.
|
181
|
+
# This method can update most, but not all, issue fields.
|
191
182
|
#
|
192
|
-
#Fields known to not update via this method:
|
183
|
+
# Fields known to not update via this method:
|
193
184
|
# - status - use {#progress_workflow_action}
|
194
185
|
# - attachments - use {#add_base64_encoded_attachment_to_issue}
|
195
186
|
#
|
196
|
-
#Though JIRA::FieldValue objects have an id field, they do not expect
|
197
|
-
#
|
198
|
-
|
187
|
+
# Though JIRA::FieldValue objects have an id field, they do not expect to be
|
188
|
+
# given id values. You must use the name of the field you wish to update.
|
189
|
+
# @example Usage With A Normal Field
|
199
190
|
# summary = JIRA::FieldValue.new
|
200
191
|
# summary.id = 'summary'
|
201
192
|
# summary.values = ['My new summary']
|
202
|
-
|
193
|
+
# @example Usage With A Custom Field
|
203
194
|
# custom_field = JIRA::FieldValue.new
|
204
195
|
# custom_field.id = 'customfield_10060'
|
205
196
|
# custom_field.values = ['123456']
|
206
|
-
|
197
|
+
# @example Setting a field to be blank/nil
|
207
198
|
# description = JIRA::FieldValue.field_value_with_nil_values 'description'
|
208
|
-
|
199
|
+
# @example Calling the method to update an issue
|
209
200
|
# jira_service_instance.update_issue 'PROJECT-1', description, custom_field
|
210
|
-
|
211
|
-
|
212
|
-
|
201
|
+
# @param [String] issue_key
|
202
|
+
# @param [JIRA::FieldValue] *field_values
|
203
|
+
# @return [JIRA::Issue]
|
213
204
|
def update_issue(issue_key, *field_values)
|
214
205
|
response = invoke('soap:updateIssue') { |msg|
|
215
206
|
msg.add 'soap:in0', @auth_token
|
@@ -222,16 +213,16 @@ module RemoteAPI
|
|
222
213
|
JIRA::Issue.issue_with_xml_fragment frag
|
223
214
|
end
|
224
215
|
|
225
|
-
#Some fields will be ignored when an issue is created
|
216
|
+
# Some fields will be ignored when an issue is created.
|
226
217
|
# - reporter - you cannot override this value at creation
|
227
|
-
# -
|
218
|
+
# - resolution
|
228
219
|
# - attachments
|
229
220
|
# - votes
|
230
221
|
# - status
|
231
|
-
# -
|
222
|
+
# - due date - I think this is a bug in jiraSOAP or JIRA
|
232
223
|
# - environment - I think this is a bug in jiraSOAP or JIRA
|
233
|
-
|
234
|
-
|
224
|
+
# @param [JIRA::Issue] issue
|
225
|
+
# @return [JIRA::Issue]
|
235
226
|
def create_issue_with_issue(issue)
|
236
227
|
response = invoke('soap:createIssue') { |msg|
|
237
228
|
msg.add 'soap:in0', @auth_token
|
@@ -242,39 +233,349 @@ module RemoteAPI
|
|
242
233
|
frag = response.document.xpath '//createIssueReturn'
|
243
234
|
JIRA::Issue.issue_with_xml_fragment frag
|
244
235
|
end
|
236
|
+
|
237
|
+
# @todo test this method
|
238
|
+
# @param [String] issue_key
|
239
|
+
# @return [JIRA::Issue]
|
240
|
+
def get_issue_with_key(issue_key)
|
241
|
+
response = invoke('soap:getIssue') { |msg|
|
242
|
+
msg.add 'soap:in0', @auth_token
|
243
|
+
msg.add 'soap:in1', issue_key
|
244
|
+
}
|
245
|
+
frag = response.document.xpath '//getIssueReturn'
|
246
|
+
JIRA:Issue.issue_with_xml_fragment frag
|
247
|
+
end
|
248
|
+
|
249
|
+
# @todo test this method
|
250
|
+
# @param [String] issue_id
|
251
|
+
# @return [JIRA::Issue]
|
252
|
+
def get_issue_with_id(issue_id)
|
253
|
+
response = invoke('soap:getIssueById') { |msg|
|
254
|
+
msg.add 'soap:in0', @auth_token
|
255
|
+
msg.add 'soap:in1', issue_id
|
256
|
+
}
|
257
|
+
frag = response.document.xpath '//getIssueByIdReturn'
|
258
|
+
JIRA::Issue.issue_with_xml_fragment frag
|
259
|
+
end
|
260
|
+
|
261
|
+
# @todo test this method
|
262
|
+
# @param [String] issue_key
|
263
|
+
# @return [[JIRA::Attachment]]
|
264
|
+
def get_attachments_for_issue_with_key(issue_key)
|
265
|
+
response = invoke('soap:getAttachmentsFromIssue') { |msg|
|
266
|
+
msg.add 'soap:in0', @auth_token
|
267
|
+
msg.add 'soap:in1', issue_key
|
268
|
+
}
|
269
|
+
response.document.xpath("#{RESPONSE_XPATH}/getAttachmentsFromIssueReturn").map {
|
270
|
+
|frag|
|
271
|
+
JIRA::AttachmentMetadata.attachment_with_xml_fragment frag
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
# @todo test this method
|
276
|
+
# @param [String] project_key
|
277
|
+
# @param [JIRA::Version] version
|
278
|
+
# @return [JIRA::Version]
|
279
|
+
def add_version_to_project(project_key, version)
|
280
|
+
response = invoke('soap:addVersion') { |msg|
|
281
|
+
msg.add 'soap:in0', @auth_token
|
282
|
+
msg.add 'soap:in1', project_key
|
283
|
+
msg.add 'soap:in2' do |submsg| version.soapify_for submsg end
|
284
|
+
}
|
285
|
+
frag = response.document.xpath '//addVersionReturn'
|
286
|
+
JIRA::Version.version_with_xml_fragment frag
|
287
|
+
end
|
288
|
+
|
289
|
+
# @todo test this method
|
290
|
+
# @param [String] project_key
|
291
|
+
# @param [String] version_name
|
292
|
+
# @param [boolean] state
|
293
|
+
# @return [boolean] true if successful, otherwise an exception is thrown
|
294
|
+
def set_archive_state_for_version_for_project(project_key, version_name, state)
|
295
|
+
response = invoke('soap:archiveVersion') { |msg|
|
296
|
+
msg.add 'soap:in0', @auth_token
|
297
|
+
msg.add 'soap:in1', project_key
|
298
|
+
msg.add 'soap:in2', version_name
|
299
|
+
msg.add 'soap:in3', state
|
300
|
+
}
|
301
|
+
true
|
302
|
+
end
|
303
|
+
|
304
|
+
# @todo test this method
|
305
|
+
# @param [JIRA::Project] project
|
306
|
+
# @return [JIRA::Project]
|
307
|
+
def create_project_with_project(project)
|
308
|
+
response = invoke('soap:createProjectFromObject') { |msg|
|
309
|
+
msg.add 'soap:in0', @auth_token
|
310
|
+
msg.add 'soap:in1' do |submsg| project.soapify_for submsg end
|
311
|
+
}
|
312
|
+
frag = response.document.xpath '//createProjectFromObjectReturn'
|
313
|
+
JIRA::Project.project_with_xml_fragment frag
|
314
|
+
end
|
315
|
+
|
316
|
+
# @todo test this method
|
317
|
+
# @param [JIRA::Project] project
|
318
|
+
# @return [JIRA::Project]
|
319
|
+
def update_project_with_project(project)
|
320
|
+
response = invoke('soap:updateProject') { |msg|
|
321
|
+
msg.add 'soap:in0', @auth_token
|
322
|
+
msg.add 'soap:in1' do |submsg| project.soapify_for submsg end
|
323
|
+
}
|
324
|
+
frag = response.document.xpath '//updateProjectReturn'
|
325
|
+
JIRA::Project.project_with_xml_fragment frag
|
326
|
+
end
|
327
|
+
|
328
|
+
# @todo test this method
|
329
|
+
# @param [String] username
|
330
|
+
# @param [String] password
|
331
|
+
# @param [String] full_name
|
332
|
+
# @param [String] email
|
333
|
+
# @return [JIRA::User]
|
334
|
+
def create_user(username, password, full_name, email)
|
335
|
+
response = invoke('soap:createUser') { |msg|
|
336
|
+
msg.add 'soap:in0', @auth_token
|
337
|
+
msg.add 'soap:in1', username
|
338
|
+
msg.add 'soap:in2', password
|
339
|
+
msg.add 'soap:in3', full_name
|
340
|
+
msg.add 'soap:in4', email
|
341
|
+
}
|
342
|
+
frag = response.document.xpath '//createUserReturn'
|
343
|
+
JIRA::User.user_with_xml_fragment frag
|
344
|
+
end
|
345
|
+
|
346
|
+
# @todo test this method
|
347
|
+
# @param [String] username
|
348
|
+
# @return [boolean] true if successful, throws an exception otherwise
|
349
|
+
def delete_user_with_name(username)
|
350
|
+
response = invoke('soap:deleteUser') { |msg|
|
351
|
+
msg.add 'soap:in0', @auth_token
|
352
|
+
msg.add 'soap:in1', username
|
353
|
+
}
|
354
|
+
true
|
355
|
+
end
|
356
|
+
|
357
|
+
# @todo test this method
|
358
|
+
# @param [String] id
|
359
|
+
# @return [JIRA::Project]
|
360
|
+
def get_project_with_id(id)
|
361
|
+
response = invoke('soap:getProjectById') { |msg|
|
362
|
+
msg.add 'soap:in0', @auth_token
|
363
|
+
msg.add 'soap:in1', id
|
364
|
+
}
|
365
|
+
frag = response.document.xpath '//getProjectByIdReturn'
|
366
|
+
JIRA::Project.project_with_xml_fragment frag
|
367
|
+
end
|
368
|
+
|
369
|
+
# @todo test this method
|
370
|
+
# @param [String] issue_key
|
371
|
+
# @param [JIRA::Comment] comment
|
372
|
+
# @return [boolean] true if successful, throws an exception otherwise
|
373
|
+
def add_comment_to_issue(issue_key, comment)
|
374
|
+
response = invoke('soap:addComment') { |msg|
|
375
|
+
msg.add 'soap:in0', @auth_token
|
376
|
+
msg.add 'soap:in1', issue_key
|
377
|
+
msg.add 'soap:in2' do |submsg| comment.soapify_for submsg end
|
378
|
+
}
|
379
|
+
true
|
380
|
+
end
|
381
|
+
|
382
|
+
# @todo test this method
|
383
|
+
# @param [String] id
|
384
|
+
# @return [JIRA::Comment]
|
385
|
+
def get_comment_with_id(id)
|
386
|
+
response = invoke('soap:getComment') { |msg|
|
387
|
+
msg.add 'soap:in0', @auth_token
|
388
|
+
msg.add 'soap:in1', id
|
389
|
+
}
|
390
|
+
frag = response.document.xpath '//getCommentReturn'
|
391
|
+
JIRA::Comment.comment_with_xml_fragment frag
|
392
|
+
end
|
393
|
+
|
394
|
+
# @todo test this method
|
395
|
+
# @param [String] issue_key
|
396
|
+
# @return [[JIRA::Comment]]
|
397
|
+
def get_comments_for_issue(issue_key)
|
398
|
+
response = invoke('soap:getComments') { |msg|
|
399
|
+
msg.add 'soap:in0', @auth_token
|
400
|
+
msg.add 'soap:in1', issue_key
|
401
|
+
}
|
402
|
+
response.document.xpath("#{RESPONSE_XPATH}/getCommentsReturn").map {
|
403
|
+
|frag|
|
404
|
+
JIRA::Comment.comment_with_xml_fragment frag
|
405
|
+
}
|
406
|
+
end
|
407
|
+
|
408
|
+
# @todo test this method
|
409
|
+
# @param [String] project_name
|
410
|
+
# @return [[JIRA::IssueType]]
|
411
|
+
def get_issue_types_for_project_with_id(project_id)
|
412
|
+
response = invoke('soap:getIssueTypesForProject') { |msg|
|
413
|
+
msg.add 'soap:in0', @auth_token
|
414
|
+
msg.add 'soap:in1', project_id
|
415
|
+
}
|
416
|
+
response.document.xpath("#{RESPONSE_XPATH}/getIssueTypesForProjectReturn").map {
|
417
|
+
|frag|
|
418
|
+
JIRA::IssueType.issue_type_with_xml_fragment frag
|
419
|
+
}
|
420
|
+
end
|
421
|
+
|
422
|
+
# @todo test this method
|
423
|
+
# @param [String] query
|
424
|
+
# @param [Fixnum] max_results
|
425
|
+
# @param [Fixnum] offset
|
426
|
+
# @return [[JIRA::Issue]]
|
427
|
+
def get_issues_from_text_search(query, max_results = 500, offset = 0)
|
428
|
+
response = invoke('soap:getIssuesFromTextSearch') { |msg|
|
429
|
+
msg.add 'soap:in0', @auth_token
|
430
|
+
msg.add 'soap:in1', query
|
431
|
+
msg.add 'soap:in2', offset
|
432
|
+
msg.add 'soap:in3', max_results
|
433
|
+
}
|
434
|
+
response.document.xpath("#{RESPONSE_XPATH}/getIssuesFromTextSearchWithLimitReturn").map {
|
435
|
+
|frag|
|
436
|
+
JIRA::Issue.issue_with_xml_fragment frag
|
437
|
+
}
|
438
|
+
end
|
439
|
+
|
440
|
+
# @todo test this method
|
441
|
+
# @param [JIRA::Comment] comment
|
442
|
+
# @return [JIRA::Comment]
|
443
|
+
def update_comment(comment)
|
444
|
+
response = invoke('soap:editComment') { |msg|
|
445
|
+
msg.add 'soap:in0', @auth_token
|
446
|
+
msg.add 'soap:in1' do |submsg| comment.soapify_for submsg end
|
447
|
+
}
|
448
|
+
frag = response.document.xpath '//editCommentReturn'
|
449
|
+
JIRA::Comment.comment_with_xml_fragment frag
|
450
|
+
end
|
451
|
+
|
452
|
+
# @todo test this method
|
453
|
+
# @return [[JIRA::IssueType]]
|
454
|
+
def get_subtask_issue_types
|
455
|
+
response = invoke('soap:getSubTaskIssueTypes') { |msg|
|
456
|
+
msg.add 'soap:in0', @auth_token
|
457
|
+
}
|
458
|
+
response.document.xpath("#{RESPONSE_XPATH}/getSubtaskIssueTypesReturn").map {
|
459
|
+
|frag|
|
460
|
+
JIRA::IssueType.issue_type_with_xml_fragment frag
|
461
|
+
}
|
462
|
+
end
|
463
|
+
|
464
|
+
# @todo test this method
|
465
|
+
# @param [String] project_id
|
466
|
+
# @return [[JIRA::IssueType]]
|
467
|
+
def get_subtask_issue_types_for_project_with_id(project_id)
|
468
|
+
response = invoke('soap:getSubTaskIssueTypesForProject') { |msg|
|
469
|
+
msg.add 'soap:in0', @auth_token
|
470
|
+
msg.add 'soap:in1', project_id
|
471
|
+
}
|
472
|
+
response.document.xpath("#{RESPONSE_XPATH}/getSubtaskIssueTypesForProjectReturn").map {
|
473
|
+
|frag|
|
474
|
+
JIRA::IssueType.issue_type_with_xml_fragment frag
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
# @todo test this method
|
479
|
+
# @todo find out what this method does
|
480
|
+
# @return [boolean] true if successful, throws an exception otherwise
|
481
|
+
def refresh_custom_fields
|
482
|
+
response = invoke('soap:refreshCustomFields') { |msg|
|
483
|
+
msg.add 'soap:in0', @auth_token
|
484
|
+
}
|
485
|
+
true
|
486
|
+
end
|
487
|
+
|
488
|
+
# @todo test this method
|
489
|
+
# Retrieves favourite filters for the currently logged in user.
|
490
|
+
# @return [JIRA::Filter]
|
491
|
+
def get_favourite_filters
|
492
|
+
response = invoke('soap:getFavouriteFilters') { |msg|
|
493
|
+
msg.add 'soap:in0', @auth_token
|
494
|
+
}
|
495
|
+
response.document.xpath("#{RESPONSE_XPATH}/getFavouriteFiltersReturn").map {
|
496
|
+
|frag|
|
497
|
+
JIRA::Filter.filter_with_xml_fragment frag
|
498
|
+
}
|
499
|
+
end
|
500
|
+
|
501
|
+
# @todo test this method
|
502
|
+
# @param [String] id
|
503
|
+
# @param [Fixnum] max_results
|
504
|
+
# @param [Fixnum] offset
|
505
|
+
# @return [[JIRA::Issue]]
|
506
|
+
def get_issues_from_filter_with_id(id, max_results = 500, offset = 0)
|
507
|
+
response = invoke('soap:getIssuesFromFilterWithLimit') { |msg|
|
508
|
+
msg.add 'soap:in0', @auth_token
|
509
|
+
msg.add 'soap:in1', id
|
510
|
+
msg.add 'soap:in2', offset
|
511
|
+
msg.add 'soap:in3', max_results
|
512
|
+
}
|
513
|
+
response.document.xpath("#{RESPONSE_XPATH}/getIssuesFromFilterWithLimitReturn").map {
|
514
|
+
|frag|
|
515
|
+
JIRA::Issue.issue_with_xml_fragment frag
|
516
|
+
}
|
517
|
+
end
|
518
|
+
|
519
|
+
# @todo test this method
|
520
|
+
# @param [String] project_name
|
521
|
+
# @param [JIRA::Version] version
|
522
|
+
# @return [boolean] true, throws an exception otherwise
|
523
|
+
def release_version_for_project(project_name, version)
|
524
|
+
response = invoke('soap:releaseVersion') { |msg|
|
525
|
+
msg.add 'soap:in0', @auth_token
|
526
|
+
msg.add 'soap:in1', project_name
|
527
|
+
msg.add 'soap:in2', version.id
|
528
|
+
}
|
529
|
+
true
|
530
|
+
end
|
531
|
+
|
532
|
+
# @todo test this method
|
533
|
+
# @param [String] id
|
534
|
+
# @return [Fixnum]
|
535
|
+
def get_issue_count_for_filter_with_id(id)
|
536
|
+
response = invoke('soap:getIssueCountForFilter') { |msg|
|
537
|
+
msg.add 'soap:in0', @auth_token
|
538
|
+
msg.add 'soap:in1', id
|
539
|
+
}
|
540
|
+
response.document.xpath('//getIssueCountForFilterReturn').to_s.to_i
|
541
|
+
end
|
542
|
+
|
543
|
+
# @todo test this method
|
544
|
+
# @todo optimize building the message, try a single pass
|
545
|
+
# Expect this method to be slow.
|
546
|
+
# @param [String] issue_key
|
547
|
+
# @param [[String]] filenames names to put on the files
|
548
|
+
# @param [[String]] data base64 encoded data
|
549
|
+
# @return [boolean] true if successful, otherwise an exception is thrown
|
550
|
+
def add_base64_encoded_attachments_to_issue(issue_key, filenames, data)
|
551
|
+
response = invoke('soap:addBase64EncodedAttachmentsToIssue') { |msg|
|
552
|
+
msg.add 'soap:in0', @auth_token
|
553
|
+
msg.add 'soap:in1', issue_key
|
554
|
+
msg.add 'soap:in2' do |submsg|
|
555
|
+
filenames.each { |filename| submsg.add 'filenames', filename }
|
556
|
+
end
|
557
|
+
msg.add 'soap:in3' do |submsg|
|
558
|
+
data.each { |datum| submsg.add 'base64EncodedData', datum }
|
559
|
+
end
|
560
|
+
}
|
561
|
+
true
|
562
|
+
end
|
563
|
+
|
564
|
+
# @todo test this method
|
565
|
+
# @return [JIRA::ServerInfo]
|
566
|
+
def get_server_info
|
567
|
+
response = invoke('soap:getServerInfo') { |msg|
|
568
|
+
msg.add 'soap:in0', @auth_token
|
569
|
+
}
|
570
|
+
frag = response.document.xpath '//getServerInfoReturn'
|
571
|
+
JIRA::ServerInfo.server_info_with_xml_fragment frag
|
572
|
+
end
|
245
573
|
end
|
246
574
|
|
247
|
-
#TODO:
|
248
|
-
# addBase64EncodedAttachmentsToIssue
|
249
|
-
# addComment
|
250
|
-
# addVersion
|
251
|
-
# archiveVersion
|
252
|
-
# createProject
|
575
|
+
#TODO: v0.3
|
253
576
|
# createProjectRole
|
254
|
-
# createUser
|
255
577
|
# deleteProjectAvatar
|
256
|
-
# deleteUser
|
257
|
-
# editComment
|
258
|
-
# getAttachmentsFromIssue
|
259
578
|
# getAvailableActions
|
260
|
-
# getComment
|
261
|
-
# getComments
|
262
|
-
# getComponents
|
263
|
-
# getFavouriteFilters
|
264
|
-
# getIssue
|
265
|
-
# getIssueById
|
266
|
-
# getIssueCountForFilter
|
267
|
-
# getIssuesFromFilterWithLimit
|
268
|
-
# getIssuesFromTextSearchWithLimit
|
269
|
-
# getIssueTypesForProject
|
270
|
-
# getProjectById
|
271
|
-
# getServerInfo
|
272
|
-
# getSubTaskIssueTypes
|
273
|
-
# getSubTaskIssueTypesForProject
|
274
579
|
# progressWorkflowAction
|
275
|
-
# refreshCustomFields
|
276
|
-
# releaseVersion
|
277
580
|
# setProjectAvatar (change to different existing)
|
278
581
|
# setNewProjectAvatar (upload new and set it)
|
279
|
-
# updateProject
|
280
|
-
# progressWorkflowAction
|
@@ -1,34 +1,50 @@
|
|
1
1
|
module JIRA
|
2
2
|
|
3
|
+
# Represents a priority level. Straightforward.
|
3
4
|
class Priority
|
4
5
|
attr_accessor :id, :name, :color, :icon, :description
|
6
|
+
|
7
|
+
# Factory method that takes a fragment of a SOAP response.
|
8
|
+
# @todo change @color to be some kind of hex Fixnum object
|
9
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
10
|
+
# @return [JIRA::Priority,nil]
|
5
11
|
def self.priority_with_xml_fragment(frag)
|
6
12
|
return if frag.nil?
|
7
13
|
priority = Priority.new
|
8
14
|
priority.id = frag.xpath('id').to_s
|
9
15
|
priority.name = frag.xpath('name').to_s
|
10
|
-
priority.color = frag.xpath('color').to_s
|
11
|
-
priority.icon = frag.xpath('icon').to_s
|
16
|
+
priority.color = frag.xpath('color').to_s
|
17
|
+
priority.icon = URL.new frag.xpath('icon').to_s
|
12
18
|
priority.description = frag.xpath('description').to_s
|
13
19
|
priority
|
14
20
|
end
|
15
21
|
end
|
16
22
|
|
23
|
+
# Represents a resolution. Straightforward.
|
17
24
|
class Resolution
|
18
25
|
attr_accessor :id, :name, :icon, :description
|
26
|
+
|
27
|
+
# Factory method that takes a fragment of a SOAP response.
|
28
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
29
|
+
# @return [JIRA::Resolution,nil]
|
19
30
|
def self.resolution_with_xml_fragment(frag)
|
20
31
|
return if frag.nil?
|
21
32
|
resolution = Resolution.new
|
22
33
|
resolution.id = frag.xpath('id').to_s
|
23
34
|
resolution.name = frag.xpath('name').to_s
|
24
|
-
resolution.icon = frag.xpath('icon').to_s
|
35
|
+
resolution.icon = URL.new frag.xpath('icon').to_s
|
25
36
|
resolution.description = frag.xpath('description').to_s
|
26
37
|
resolution
|
27
38
|
end
|
28
39
|
end
|
29
40
|
|
41
|
+
# Represents a field mapping.
|
30
42
|
class Field
|
31
43
|
attr_accessor :id, :name
|
44
|
+
|
45
|
+
# Factory method that takes a fragment of a SOAP response.
|
46
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
47
|
+
# @return [JIRA::Field,nil]
|
32
48
|
def self.field_with_xml_fragment(frag)
|
33
49
|
return if frag.nil?
|
34
50
|
field = Field.new
|
@@ -38,8 +54,14 @@ class Field
|
|
38
54
|
end
|
39
55
|
end
|
40
56
|
|
57
|
+
# Represents a custom field with values.
|
58
|
+
# @todo see if @key is always nil from the server
|
41
59
|
class CustomField
|
42
60
|
attr_accessor :id, :key, :values
|
61
|
+
|
62
|
+
# Factory method that takes a fragment of a SOAP response.
|
63
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
64
|
+
# @return [JIRA::CustomField,nil]
|
43
65
|
def self.custom_field_with_xml_fragment(frag)
|
44
66
|
return if frag.nil?
|
45
67
|
custom_field = CustomField.new
|
@@ -48,65 +70,145 @@ class CustomField
|
|
48
70
|
custom_field.values = frag.xpath('values/*').map { |value| value.to_s }
|
49
71
|
custom_field
|
50
72
|
end
|
73
|
+
|
74
|
+
# Generate a SOAP message fragment for the object.
|
75
|
+
# @param [Handsoap::XmlMason::Node] msg SOAP message to add the object to
|
76
|
+
# @param [String] label tag name used in wrapping tags
|
77
|
+
# @return [Handsoap::XmlMason::Element]
|
51
78
|
def soapify_for(msg, label = 'customFieldValues')
|
52
79
|
msg.add label do |submsg|
|
53
80
|
submsg.add 'customfieldId', @id
|
54
|
-
submsg.add 'key', @key
|
81
|
+
submsg.add 'key', @key
|
55
82
|
submsg.add_simple_array 'values', @values
|
56
83
|
end
|
57
84
|
end
|
58
85
|
end
|
59
86
|
|
87
|
+
# Represents and issue type. Straight forward.
|
60
88
|
class IssueType
|
61
89
|
attr_accessor :id, :name, :icon, :description
|
62
90
|
attr_writer :subtask
|
91
|
+
|
92
|
+
# @return [boolean] true if the issue type is a subtask, otherwise false
|
63
93
|
def subtask?; @subtask; end
|
94
|
+
|
95
|
+
# Factory method that takes a fragment of a SOAP response.
|
96
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
97
|
+
# @return [JIRA::IssueType,nil]
|
64
98
|
def self.issue_type_with_xml_fragment(frag)
|
65
99
|
return if frag.nil?
|
66
100
|
issue_type = IssueType.new
|
67
101
|
issue_type.id = frag.xpath('id').to_s
|
68
102
|
issue_type.name = frag.xpath('name').to_s
|
69
|
-
issue_type.icon = frag.xpath('icon').to_s
|
103
|
+
issue_type.icon = URL.new frag.xpath('icon').to_s
|
70
104
|
issue_type.subtask = frag.xpath('subtask').to_s == 'true'
|
71
105
|
issue_type.description = frag.xpath('description').to_s
|
72
106
|
issue_type
|
73
107
|
end
|
74
108
|
end
|
75
109
|
|
110
|
+
# Represents a comment. Straight forward.
|
111
|
+
class Comment
|
112
|
+
attr_accessor :id, :original_author, :role_level, :group_level, :body
|
113
|
+
attr_accessor :create_date, :last_updated, :update_author
|
114
|
+
|
115
|
+
# Factory method that takes a fragment of a SOAP response.
|
116
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
117
|
+
# @return [JIRA::Comment,nil]
|
118
|
+
def self.comment_with_xml_fragment(frag)
|
119
|
+
return if frag.nil?
|
120
|
+
comment = Comment.new
|
121
|
+
comment.id = frag.xpath('id').to_s
|
122
|
+
comment.original_author = frag.xpath('author').to_s
|
123
|
+
comment.body = frag.xpath('body').to_s
|
124
|
+
comment.create_date = Time.xmlschema frag.xpath('created').to_s
|
125
|
+
comment.group_level = frag.xpath('updateAuthor').to_s
|
126
|
+
comment.role_level = frag.xpath('roleLevel').to_s
|
127
|
+
comment.update_author = frag.xpath('updateAuthor').to_s
|
128
|
+
comment.last_updated = Time.xmlschema frag.xpath('updated').to_s
|
129
|
+
comment
|
130
|
+
end
|
131
|
+
|
132
|
+
# @param [Handsoap::XmlMason::Node] msg
|
133
|
+
# @return [Handsoap::XmlMason::Node]
|
134
|
+
def soapify_for(msg)
|
135
|
+
msg.add 'id', @id
|
136
|
+
msg.add 'author', @original_author
|
137
|
+
msg.add 'body', @body
|
138
|
+
msg.add 'created', @created
|
139
|
+
msg.add 'groupLevel', @group_level
|
140
|
+
msg.add 'roleLevel', @role_level
|
141
|
+
msg.add 'updateAuthor', @update_author
|
142
|
+
msg.add 'updated', @last_updated
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Represents a status. Straightforward.
|
76
147
|
class Status
|
77
148
|
attr_accessor :id, :name, :icon, :description
|
149
|
+
|
150
|
+
# Factory method that takes a fragment of a SOAP response.
|
151
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
152
|
+
# @return [JIRA::Status,nil]
|
78
153
|
def self.status_with_xml_fragment(frag)
|
79
154
|
return if frag.nil?
|
80
155
|
status = Status.new
|
81
156
|
status.id = frag.xpath('id').to_s
|
82
157
|
status.name = frag.xpath('name').to_s
|
83
|
-
status.icon = frag.xpath('icon').to_s
|
158
|
+
status.icon = URL.new frag.xpath('icon').to_s
|
84
159
|
status.description = frag.xpath('description').to_s
|
85
160
|
status
|
86
161
|
end
|
87
162
|
end
|
88
163
|
|
164
|
+
# Represents a version for a project. Straightforward.
|
165
|
+
# @todo find out why we don't get a description for this object
|
89
166
|
class Version
|
90
167
|
attr_accessor :id, :name, :sequence, :released, :archived, :release_date
|
91
168
|
attr_writer :released, :archived
|
169
|
+
|
170
|
+
# @return [boolean] true if the version has been released, otherwise false
|
92
171
|
def released?; @released; end
|
172
|
+
|
173
|
+
# @return [boolean] true if the version has been archive, otherwise false
|
93
174
|
def archived?; @archived; end
|
175
|
+
|
176
|
+
# Factory method that takes a fragment of a SOAP response.
|
177
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
178
|
+
# @return [JIRA::Status,nil]
|
94
179
|
def self.version_with_xml_fragment(frag)
|
95
180
|
return if frag.nil?
|
96
|
-
#TODO: find out why we don't get a description for this type
|
97
181
|
version = Version.new
|
98
182
|
version.id = frag.xpath('id').to_s
|
99
183
|
version.name = frag.xpath('name').to_s
|
100
184
|
version.sequence = frag.xpath('sequence').to_s.to_i
|
101
185
|
version.released = frag.xpath('released').to_s == 'true'
|
102
186
|
version.archived = frag.xpath('archived').to_s == 'true'
|
103
|
-
version.release_date = frag.xpath('releaseDate')
|
187
|
+
version.release_date = Time.xmlschema frag.xpath('releaseDate')
|
104
188
|
version
|
105
189
|
end
|
190
|
+
|
191
|
+
# @param [Handsoap::XmlMason::Node] msg
|
192
|
+
# @return [Handsoap::XmlMason::Node]
|
193
|
+
def soapify_for(msg)
|
194
|
+
msg.add 'id', @id
|
195
|
+
msg.add 'name', @name
|
196
|
+
msg.add 'sequence', @sequence
|
197
|
+
msg.add 'released', @released
|
198
|
+
msg.add 'archived', @archived
|
199
|
+
msg.add 'releaseDate', @release_date
|
200
|
+
end
|
106
201
|
end
|
107
202
|
|
203
|
+
# Represents a scheme used by the server. Not very useful for the sake of the
|
204
|
+
# API; a more useful case might be if you wanted to emulate the server's
|
205
|
+
# behaviour.
|
108
206
|
class Scheme
|
109
207
|
attr_accessor :id, :name, :type, :description
|
208
|
+
|
209
|
+
# Factory method that takes a fragment of a SOAP response.
|
210
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
211
|
+
# @return [JIRA::Scheme,nil]
|
110
212
|
def self.scheme_with_xml_fragment(frag)
|
111
213
|
return if frag.nil?
|
112
214
|
scheme = Scheme.new
|
@@ -118,8 +220,13 @@ class Scheme
|
|
118
220
|
end
|
119
221
|
end
|
120
222
|
|
223
|
+
# Represents a component description for a project. Straightforward.
|
121
224
|
class Component
|
122
225
|
attr_accessor :id, :name
|
226
|
+
|
227
|
+
# Factory method that takes a fragment of a SOAP response.
|
228
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
229
|
+
# @return [JIRA::Component,nil]
|
123
230
|
def self.component_with_xml_fragment(frag)
|
124
231
|
return if frag.nil?
|
125
232
|
component = Component.new
|
@@ -129,20 +236,25 @@ class Component
|
|
129
236
|
end
|
130
237
|
end
|
131
238
|
|
239
|
+
# Represents a project configuration. NOT straightforward.
|
240
|
+
# @todo find out why the server always seems to pass nil for schemes
|
132
241
|
class Project
|
133
242
|
attr_accessor :id, :name, :key, :url, :project_url, :lead, :description
|
134
243
|
attr_accessor :issue_security_scheme, :notification_scheme, :permission_scheme
|
244
|
+
|
245
|
+
# Factory method that takes a fragment of a SOAP response.
|
246
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
247
|
+
# @return [JIRA::Project,nil]
|
135
248
|
def self.project_with_xml_fragment(frag)
|
136
249
|
return if frag.nil?
|
137
250
|
project = Project.new
|
138
251
|
project.id = frag.xpath('id').to_s
|
139
252
|
project.name = frag.xpath('name').to_s
|
140
253
|
project.key = frag.xpath('key').to_s
|
141
|
-
project.url = frag.xpath('url').to_s
|
142
|
-
project.project_url = frag.xpath('projectUrl').to_s
|
254
|
+
project.url = URL.new frag.xpath('url').to_s
|
255
|
+
project.project_url = URL.new frag.xpath('projectUrl').to_s
|
143
256
|
project.lead = frag.xpath('lead').to_s
|
144
257
|
project.description = frag.xpath('description').to_s
|
145
|
-
#TODO: find out why the server always seems to pass nil
|
146
258
|
project.issue_security_scheme =
|
147
259
|
Scheme.scheme_with_xml_fragment frag.xpath 'issueSecurityScheme'
|
148
260
|
project.notification_scheme =
|
@@ -151,12 +263,32 @@ class Project
|
|
151
263
|
Scheme.scheme_with_xml_fragment frag.xpath 'permissionScheme'
|
152
264
|
project
|
153
265
|
end
|
266
|
+
|
267
|
+
# @todo encode the schemes
|
268
|
+
# @param [Handsoap::XmlMason::Node] msg
|
269
|
+
# @return [Handsoap::XmlMason::Node]
|
270
|
+
def soapify_for(msg)
|
271
|
+
msg.add 'id', @id
|
272
|
+
msg.add 'name', @name
|
273
|
+
msg.add 'key', @key
|
274
|
+
msg.add 'url', @url
|
275
|
+
msg.add 'projectUrl', @project_url
|
276
|
+
msg.add 'lead', @lead
|
277
|
+
msg.add 'description', @description
|
278
|
+
end
|
154
279
|
end
|
155
280
|
|
281
|
+
# Contains a base64 encoded avatar image and some metadata. Straightforward.
|
156
282
|
class Avatar
|
157
283
|
attr_accessor :id, :owner, :type, :content_type, :base64_data
|
158
284
|
attr_writer :system
|
285
|
+
|
286
|
+
# @return [boolean] true if avatar is the default system avatar, else false
|
159
287
|
def system?; @system; end
|
288
|
+
|
289
|
+
# Factory method that takes a fragment of a SOAP response.
|
290
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
291
|
+
# @return [JIRA::Avatar,nil]
|
160
292
|
def self.avatar_with_xml_fragment(frag)
|
161
293
|
return if frag.nil?
|
162
294
|
avatar = Avatar.new
|
@@ -170,14 +302,24 @@ class Avatar
|
|
170
302
|
end
|
171
303
|
end
|
172
304
|
|
173
|
-
#easily the most convoluted structure in the API
|
174
|
-
#will most likely be the
|
305
|
+
# Represents a JIRA issue; easily the most convoluted structure in the API.
|
306
|
+
# This structure and anything related directly to it will most likely be the
|
307
|
+
# greatest source of bugs.
|
308
|
+
#
|
309
|
+
# The irony of the situation is that this structure is also the most critical
|
310
|
+
# to have in working order.
|
311
|
+
#
|
312
|
+
# Issues with an UNRESOLVED status will have nil for the value of @resolution.
|
175
313
|
class Issue
|
176
314
|
attr_accessor :id, :key, :summary, :description, :type_id, :last_updated
|
177
315
|
attr_accessor :votes, :status_id, :assignee_name, :reporter_name, :priority_id
|
178
316
|
attr_accessor :project_name, :affects_versions, :create_date, :due_date
|
179
317
|
attr_accessor :fix_versions, :resolution_id, :environment, :components
|
180
318
|
attr_accessor :attachment_names, :custom_field_values
|
319
|
+
|
320
|
+
# Factory method that takes a fragment of a SOAP response.
|
321
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
322
|
+
# @return [JIRA::Issue,nil]
|
181
323
|
def self.issue_with_xml_fragment(frag)
|
182
324
|
return if frag.nil?
|
183
325
|
issue = Issue.new
|
@@ -201,28 +343,42 @@ class Issue
|
|
201
343
|
issue.summary = frag.xpath('summary').to_s
|
202
344
|
issue.description = frag.xpath('description').to_s
|
203
345
|
issue.type_id = frag.xpath('type').to_s
|
204
|
-
issue.last_updated = frag.xpath('updated').to_s
|
346
|
+
issue.last_updated = Time.xmlschema frag.xpath('updated').to_s
|
205
347
|
issue.votes = frag.xpath('votes').to_s.to_i
|
206
348
|
issue.status_id = frag.xpath('status').to_s
|
207
349
|
issue.assignee_name = frag.xpath('assignee').to_s
|
208
350
|
issue.reporter_name = frag.xpath('reporter').to_s
|
209
351
|
issue.priority_id = frag.xpath('priority').to_s
|
210
352
|
issue.project_name = frag.xpath('project').to_s
|
211
|
-
issue.create_date = frag.xpath('created').to_s
|
212
|
-
issue.due_date = frag.xpath('duedate').to_s
|
353
|
+
issue.create_date = Time.xmlschema frag.xpath('created').to_s
|
354
|
+
issue.due_date = Time.xmlschema frag.xpath('duedate').to_s
|
213
355
|
issue.resolution_id = frag.xpath('resolution').to_s
|
214
356
|
issue.environment = frag.xpath('environment').to_s
|
215
357
|
issue
|
216
358
|
end
|
217
|
-
#can you spot the oddities and inconsistencies? (hint: there are many)
|
218
|
-
def soapify_for(msg)
|
219
|
-
#we don't both including fields that are ignored
|
220
|
-
#I tried to only ignore fields that will never be needed at creation
|
221
|
-
#but I may have messed up. It should be easy to fix :)
|
222
|
-
#we don't wrap the whole thing in 'issue' tags for #create_issue calls
|
223
|
-
#this is an inconsistency in the way jiraSOAP works
|
224
|
-
#but you'll probably never know unless you read the source
|
225
359
|
|
360
|
+
# Generate the SOAP message fragment for an issue. Can you spot the oddities
|
361
|
+
# and inconsistencies? (hint: there are many).
|
362
|
+
#
|
363
|
+
# We don't bother including fields that are ignored. I tried to only
|
364
|
+
# ignore fields that will never be needed at creation time, but I may have
|
365
|
+
# messed up.
|
366
|
+
#
|
367
|
+
# We don't wrap the whole thing in 'issue' tags for
|
368
|
+
# {#RemoteAPI#create_issue_with_issue} calls; this is an inconsistency in the
|
369
|
+
# way jiraSOAP works and may need to be worked around for other {RemoteAPI}
|
370
|
+
# methods.
|
371
|
+
#
|
372
|
+
# Servers only seem to accept issues if components/versions are just ids
|
373
|
+
# and do not contain the rest of the {JIRA::Component}/{JIRA::Version}
|
374
|
+
# structure.
|
375
|
+
#
|
376
|
+
# To get the automatic assignee we pass '-1' as the value for @assignee.
|
377
|
+
#
|
378
|
+
# Passing an environment/due date field with a value of nil causes the
|
379
|
+
# server to complain about the formatting of the message.
|
380
|
+
# @param [Handsoap::XmlMason::Node] msg message the node to add the object to
|
381
|
+
def soapify_for(msg)
|
226
382
|
#might be going away, since it appears to have no effect at creation time
|
227
383
|
msg.add 'reporter', @reporter_name unless @reporter.nil?
|
228
384
|
|
@@ -233,7 +389,6 @@ class Issue
|
|
233
389
|
msg.add 'summary', @summary
|
234
390
|
msg.add 'description', @description
|
235
391
|
|
236
|
-
#server only accepts issues if components/versions are just ids
|
237
392
|
msg.add 'components' do |submsg|
|
238
393
|
(@components || []).each { |component|
|
239
394
|
submsg.add 'components' do |component_msg|
|
@@ -255,19 +410,21 @@ class Issue
|
|
255
410
|
}
|
256
411
|
end
|
257
412
|
|
258
|
-
#-1 is the value you send to get the automatic assignee
|
259
413
|
msg.add 'assignee', (@assignee_name || '-1')
|
260
|
-
|
261
414
|
msg.add_complex_array 'customFieldValues', (@custom_field_values || [])
|
262
415
|
|
263
|
-
#passing environment/due_date when nil seems to mess up the server
|
264
416
|
msg.add 'environment', @environment unless @environment.nil?
|
265
|
-
msg.add 'duedate', @due_date unless @due_date.nil?
|
417
|
+
msg.add 'duedate', @due_date.xmlschema unless @due_date.nil?
|
266
418
|
end
|
267
419
|
end
|
268
420
|
|
421
|
+
# Contains the basic information about a user. Straightforward.
|
269
422
|
class User
|
270
423
|
attr_accessor :name, :full_name, :email
|
424
|
+
|
425
|
+
# Factory method that takes a fragment of a SOAP response.
|
426
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
427
|
+
# @return [JIRA::User,nil]
|
271
428
|
def self.user_with_xml_fragment(frag)
|
272
429
|
return if frag.nil?
|
273
430
|
user = User.new
|
@@ -278,14 +435,25 @@ class User
|
|
278
435
|
end
|
279
436
|
end
|
280
437
|
|
438
|
+
# A structure that is a bit of a hack. It is essentially just a key-value pair
|
439
|
+
# that is used mainly by {RemoteAPI#update_issue}.
|
281
440
|
class FieldValue
|
282
441
|
attr_accessor :id, :values
|
442
|
+
|
443
|
+
# Factory method that gives you a nil value for the given id.
|
444
|
+
# @param [String] id name of the field for @values
|
445
|
+
# @return [JIRA::FieldValue] Will always have @values = [nil]
|
283
446
|
def self.field_value_with_nil_values(id)
|
284
447
|
fv = FieldValue.new
|
285
448
|
fv.id = id
|
286
449
|
fv.values = [nil]
|
287
450
|
fv
|
288
451
|
end
|
452
|
+
|
453
|
+
# Generate the SOAP message fragment for a field value.
|
454
|
+
# @param [Handsoap::XmlMason::Node] message the node to add the object to
|
455
|
+
# @param [String] label name for the tags that wrap the message
|
456
|
+
# @return [Handsoap::XmlMason::Element]
|
289
457
|
def soapify_for(message, label = 'fieldValue')
|
290
458
|
message.add label do |message|
|
291
459
|
message.add 'id', @id
|
@@ -294,4 +462,85 @@ class FieldValue
|
|
294
462
|
end
|
295
463
|
end
|
296
464
|
|
465
|
+
# Only contains the meta-data for an attachment. The URI for an attachment
|
466
|
+
# appears to be of the form
|
467
|
+
# $ENDPOINT_URL/secure/attachment/$ATTACHMENT_ID/$ATTACHMENT_FILENAME
|
468
|
+
class AttachmentMetadata
|
469
|
+
attr_accessor :id, :author, :create_date, :filename, :file_size, :mime_type
|
470
|
+
|
471
|
+
# Factory method that takes a fragment of a SOAP response.
|
472
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
473
|
+
# @return [JIRA::Attachment,nil]
|
474
|
+
def self.attachment_with_xml_fragment(frag)
|
475
|
+
return if frag.nil?
|
476
|
+
attachment = Attachment.new
|
477
|
+
attachment.id = frag.xpath('id').to_s
|
478
|
+
attachment.author = frag.xpath('author').to_s
|
479
|
+
attachment.create_date = Time.xmlschema frag.xpath('created').to_s
|
480
|
+
attachment.filename = frag.xpath('filename').to_s
|
481
|
+
attachment.file_size = frag.xpath('filesize').to_s.to_i
|
482
|
+
attachment.mime_type = frag.xpath('mimetype').to_s
|
483
|
+
attachment
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# Only contains basic information about the endpoint server.
|
488
|
+
class ServerInfo
|
489
|
+
attr_accessor :base_url, :build_date, :build_number, :edition
|
490
|
+
attr_accessor :server_time, :version
|
491
|
+
|
492
|
+
# Factory method that takes a fragment of a SOAP response.
|
493
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
494
|
+
# @return [JIRA::ServerInfo,nil]
|
495
|
+
def self.server_info_with_xml_fragment(frag)
|
496
|
+
return if frag.nil?
|
497
|
+
server_info = ServerInfo.new
|
498
|
+
server_info.base_url = URL.new frag.xpath('baseUrl').to_s
|
499
|
+
server_info.build_date = Time.xmlschema frag.xpath('buildDate').to_s
|
500
|
+
server_info.build_number = frag.xpath('buildNumber').to_s.to_i
|
501
|
+
server_info.edition = frag.xpath('edition').to_s
|
502
|
+
server_info.version = frag.xpath('version').to_s
|
503
|
+
server_info.server_time =
|
504
|
+
TimeInfo.time_info_with_xml_fragment frag.xpath 'serverTime'
|
505
|
+
server_info
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# Simple structure for a time and time zone; used oddly.
|
510
|
+
class TimeInfo
|
511
|
+
attr_accessor :server_time, :timezone
|
512
|
+
|
513
|
+
# Factory method that takes a fragment of a SOAP response.
|
514
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
515
|
+
# @return [JIRA::TimeInfo,nil]
|
516
|
+
def self.time_info_with_xml_fragment(frag)
|
517
|
+
return if frag.nil?
|
518
|
+
time_info = TimeInfo.new
|
519
|
+
time_info.server_time = Time.parse frag.xpath('serverTime').to_s
|
520
|
+
time_info.timezone = frag.xpath('timeZoneId').to_s
|
521
|
+
time_info
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
# Represents a filter
|
526
|
+
# @todo find out what @project is supposed to be for
|
527
|
+
class Filter
|
528
|
+
attr_accessor :id, :name, :author, :project, :description, :xml
|
529
|
+
|
530
|
+
# Factory method that takes a fragment of a SOAP response.
|
531
|
+
# @param [Handsoap::XmlQueryFront::NokogiriDriver] frag
|
532
|
+
# @return [JIRA::Filter,nil]
|
533
|
+
def self.filter_with_xml_fragment(frag)
|
534
|
+
return if frag.nil?
|
535
|
+
filter = Filter.new
|
536
|
+
filter.id = frag.xpath('id').to_s
|
537
|
+
filter.name = frag.xpath('name').to_s
|
538
|
+
filter.author = frag.xpath('author').to_s
|
539
|
+
filter.project = frag.xpath('project').to_s
|
540
|
+
filter.description = frag.xpath('description').to_s
|
541
|
+
filter.xml = frag.xpath('xml').to_s
|
542
|
+
filter
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
297
546
|
end
|
data/lib/jiraSOAP/url.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# A bit of a hack to support using an NSURL in MacRuby while still supporting
|
2
|
+
# MRI by using the URI module.
|
3
|
+
#
|
4
|
+
# I suggest not thinking about it too much beyond this point: this is a
|
5
|
+
# URI object if you are running on CRuby, but it will be an NSURL if you
|
6
|
+
# are running on MacRuby.
|
7
|
+
class URL
|
8
|
+
attr_accessor :url
|
9
|
+
|
10
|
+
# Initializes @url with the correct object type.
|
11
|
+
# @param [String] url string to turn into some kind of URL object
|
12
|
+
# @return [URI::HTTP,NSURL] URI::HTTP on CRuby, NSURL on MacRuby
|
13
|
+
def initialize(url)
|
14
|
+
@url = URI.parse url
|
15
|
+
end
|
16
|
+
|
17
|
+
# The magic of the hack, passing everything to the level beneath.
|
18
|
+
def method_missing(method, *args)
|
19
|
+
@url.send method, *args
|
20
|
+
end
|
21
|
+
end
|
data/lib/jiraSOAP.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
#TODO: set a requirement on the handsoap version
|
2
1
|
require 'handsoap'
|
3
2
|
require 'logger'
|
3
|
+
require 'time'
|
4
|
+
require 'uri'
|
4
5
|
|
6
|
+
require 'jiraSOAP/url.rb'
|
5
7
|
require 'jiraSOAP/handsoap_extensions.rb'
|
6
8
|
require 'jiraSOAP/remoteEntities.rb'
|
7
9
|
require 'jiraSOAP/remoteAPI.rb'
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mark Rada
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-10-
|
17
|
+
date: 2010-10-12 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
35
|
+
name: nokogiri
|
36
36
|
prerelease: false
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
38
|
none: false
|
@@ -42,8 +42,34 @@ dependencies:
|
|
42
42
|
segments:
|
43
43
|
- 0
|
44
44
|
version: "0"
|
45
|
-
type: :
|
45
|
+
type: :runtime
|
46
46
|
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: minitest
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id003
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: yard
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id004
|
47
73
|
description: Written to run fast and work on Ruby 1.9 as well as MacRuby
|
48
74
|
email: mrada@marketcircle.com
|
49
75
|
executables: []
|
@@ -60,6 +86,7 @@ files:
|
|
60
86
|
- lib/jiraSOAP/macruby_stuff.rb
|
61
87
|
- lib/jiraSOAP/remoteAPI.rb
|
62
88
|
- lib/jiraSOAP/remoteEntities.rb
|
89
|
+
- lib/jiraSOAP/url.rb
|
63
90
|
- LICENSE
|
64
91
|
- README.markdown
|
65
92
|
- test/jiraSOAP_test.rb
|