jiraSOAP 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -16
- data/lib/jiraSOAP/JIRAservice.rb +21 -1
- data/lib/jiraSOAP/handsoap_extensions.rb +12 -2
- data/lib/jiraSOAP/remoteAPI.rb +93 -2
- metadata +2 -2
data/README.markdown
CHANGED
@@ -50,20 +50,7 @@ Get the [Gist](http://gist.github.com/612186).
|
|
50
50
|
Notes About Using This Gem
|
51
51
|
--------------------------
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
1. All method names have been made to feel more natural in a Ruby setting. Consult the `jiraSOAP` documentation for specifics.
|
56
|
-
|
57
|
-
2. If an API call fails with a method missing error it is because the method has not been implement, yet. I started by implementing only the methods that I needed in order to port some old scripts that ran on jira4r; other methods will be added as them gem matures (or you could add it for me :D).
|
58
|
-
|
59
|
-
3. URESOLVED issues have a Resolution with a value of `nil`.
|
60
|
-
|
61
|
-
4. To empty a field (set it to nil) you can use this pattern:
|
62
|
-
jira.update_issue 'JIRA-1', JIRA::FieldValue.fieldValueWithNilValues 'description'
|
63
|
-
|
64
|
-
5. Issue creation, using #create_issue_with_issue does not make use of all the fields in a JIRA::Issue. Which fields are used seems to depend on the version of JIRA you are connecting to.
|
65
|
-
|
66
|
-
6. RemoteAPI#update_issue wants an id for each field that you pass it, but it really wants the name of the field that you want to update. See this [Gist](http://gist.github.com/612562).
|
53
|
+
1. URESOLVED issues have a Resolution with a value of `nil`.
|
67
54
|
|
68
55
|
|
69
56
|
TODO
|
@@ -71,11 +58,9 @@ TODO
|
|
71
58
|
|
72
59
|
- Performance optimizations; there are a number of places that can be optimized
|
73
60
|
+ Using GCD/Threads for parsing arrays of results; a significant speed up for large types and large arrays (ie. creating issues from JQL searches)
|
74
|
-
- Refactor for a smaller code base
|
75
61
|
- Fix type hacks;. dates should be `NSDate`s and URLs should be `NSURL`s, right now they are all strings
|
76
62
|
- Public test suite
|
77
63
|
+ Needs a mock server
|
78
|
-
- Documentation
|
79
64
|
- Error handling
|
80
65
|
- Finish implementing all of the API
|
81
66
|
|
data/lib/jiraSOAP/JIRAservice.rb
CHANGED
@@ -1,15 +1,33 @@
|
|
1
|
+
# All the remote entities as well as the SOAP service client.
|
1
2
|
module JIRA
|
3
|
+
|
4
|
+
# Interface to the JIRA endpoint server; set at initialization.
|
5
|
+
#
|
6
|
+
# Due to limitations in Handsoap::Service, there can only be one endpoint.
|
7
|
+
# You can have multiple instances of that one endpoint if you would
|
8
|
+
# like; but if you try to set a differnt endpoint for a new instance you
|
9
|
+
# will end up messing up any other instances currently being used.
|
10
|
+
#
|
11
|
+
# It is best to treat this class as a singleton. There should only be one.
|
12
|
+
#
|
13
|
+
# HTTPS is not supported in this version.
|
2
14
|
class JIRAService < Handsoap::Service
|
3
15
|
include RemoteAPI
|
4
16
|
|
5
17
|
attr_reader :auth_token, :user
|
6
18
|
|
19
|
+
# Factory method to initialize and login.
|
20
|
+
#@param [String] url URL for the JIRA server
|
21
|
+
#@param [String] user JIRA user name to login with
|
22
|
+
#@param [String] password
|
7
23
|
def self.instance_at_url(url, user, password)
|
8
24
|
jira = JIRAService.new url
|
9
25
|
jira.login user, password
|
10
26
|
jira
|
11
27
|
end
|
12
28
|
|
29
|
+
# Slightly hacky in order to set the endpoint at the initialization.
|
30
|
+
#@param endpoint_url URL for the JIRA server
|
13
31
|
def initialize(endpoint_url)
|
14
32
|
super
|
15
33
|
|
@@ -23,8 +41,10 @@ class JIRAService < Handsoap::Service
|
|
23
41
|
|
24
42
|
#PONDER: a finalizer that will try to logout
|
25
43
|
|
44
|
+
# Something to help users out until the rest of the API is implemented.
|
26
45
|
def method_missing(method, *args)
|
27
|
-
|
46
|
+
message = 'Check the documentation; the method may not be implemented yet.'
|
47
|
+
raise NoMethodError, message, caller
|
28
48
|
end
|
29
49
|
|
30
50
|
protected
|
@@ -1,14 +1,24 @@
|
|
1
|
-
#
|
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
5
|
module XmlMason
|
6
|
+
#Represents a node in an XML document
|
4
7
|
class Node
|
5
|
-
|
8
|
+
#@todo Make this method recursive
|
9
|
+
#@param [String] node_name
|
10
|
+
#@param [Array] array
|
11
|
+
#@param [Hash] options
|
6
12
|
def add_simple_array(node_name, array = [], options = {})
|
7
13
|
prefix, name = parse_ns(node_name)
|
8
14
|
node = append_child Element.new(self, prefix, name, nil, options)
|
9
15
|
array.each { |element| node.add node_name, element }
|
10
16
|
end
|
11
17
|
|
18
|
+
#@todo Make this method recursive
|
19
|
+
#@param [String] node_name
|
20
|
+
#@param [Array] array
|
21
|
+
#@param [Hash] options
|
12
22
|
def add_complex_array(node_name, array = [], options = {})
|
13
23
|
prefix, name = parse_ns(node_name)
|
14
24
|
node = append_child Element.new(self, prefix, name, nil, options)
|
data/lib/jiraSOAP/remoteAPI.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
|
+
# Contains the API defined by Atlassian for the JIRA SOAP service. The JavaDoc
|
2
|
+
# for the SOAP API is located at http://docs.atlassian.com/software/jira/docs/api/rpc-jira-plugin/latest/com/atlassian/jira/rpc/soap/JiraSoapService.html.
|
3
|
+
#@todo exception handling
|
4
|
+
#@todo code refactoring and de-duplication
|
1
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.
|
2
9
|
RESPONSE_XPATH = '/node()[1]/node()[1]/node()[1]/node()[2]'
|
3
10
|
|
11
|
+
#The first method to call. No other method will work unless you are logged in.
|
12
|
+
#@param [String] user JIRA user name to login with
|
13
|
+
#@param [String] password
|
14
|
+
#@return [boolean] true if successful, otherwise false
|
4
15
|
def login(user, password)
|
5
16
|
response = invoke('soap:login') { |msg|
|
6
17
|
msg.add 'soap:in0', user
|
7
18
|
msg.add 'soap:in1', password
|
8
19
|
}
|
9
|
-
#TODO: error handling (catch the exception and look at the Response node?)
|
10
20
|
#cache now that we know it is safe to do so
|
11
21
|
@user = user
|
12
22
|
@auth_token = response.document.xpath('//loginReturn').first.to_s
|
13
23
|
true
|
14
24
|
end
|
15
25
|
|
26
|
+
#You only need to call this to make an exlicit logout; normally, a session
|
27
|
+
#willautomatically expire after a set time (configured on the server).
|
28
|
+
#@return [boolean] true if successful, otherwise false
|
16
29
|
def logout
|
17
30
|
response = invoke('soap:logout') { |msg|
|
18
31
|
msg.add 'soap:in0', @auth_token
|
@@ -20,6 +33,8 @@ module RemoteAPI
|
|
20
33
|
response.document.xpath('//logoutReturn').first.to_s == 'true'
|
21
34
|
end
|
22
35
|
|
36
|
+
#Get the global listing for types of priorities.
|
37
|
+
#@return [[JIRA::Priority]]
|
23
38
|
def get_priorities
|
24
39
|
response = invoke('soap:getPriorities') { |msg|
|
25
40
|
msg.add 'soap:in0', @auth_token
|
@@ -30,6 +45,8 @@ module RemoteAPI
|
|
30
45
|
}
|
31
46
|
end
|
32
47
|
|
48
|
+
#Get the global listing for types of resolutions.
|
49
|
+
#@return [[JIRA::Resolution]]
|
33
50
|
def get_resolutions
|
34
51
|
response = invoke('soap:getResolutions') { |msg|
|
35
52
|
msg.add 'soap:in0', @auth_token
|
@@ -40,6 +57,8 @@ module RemoteAPI
|
|
40
57
|
}
|
41
58
|
end
|
42
59
|
|
60
|
+
#Get the global listing for types of custom fields.
|
61
|
+
#@return [[JIRA::Field]]
|
43
62
|
def get_custom_fields
|
44
63
|
response = invoke('soap:getCustomFields') { |msg|
|
45
64
|
msg.add 'soap:in0', @auth_token
|
@@ -50,6 +69,8 @@ module RemoteAPI
|
|
50
69
|
}
|
51
70
|
end
|
52
71
|
|
72
|
+
#Get the global listing for types of issues.
|
73
|
+
#@return [[JIRA::IssueType]]
|
53
74
|
def get_issue_types
|
54
75
|
response = invoke('soap:getIssueTypes') { |msg|
|
55
76
|
msg.add 'soap:in0', @auth_token
|
@@ -60,6 +81,8 @@ module RemoteAPI
|
|
60
81
|
}
|
61
82
|
end
|
62
83
|
|
84
|
+
#Get the global listing of status type.
|
85
|
+
#@return [[JIRA::Status]]
|
63
86
|
def get_statuses
|
64
87
|
response = invoke('soap:getStatuses') { |msg|
|
65
88
|
msg.add 'soap:in0', @auth_token
|
@@ -70,6 +93,8 @@ module RemoteAPI
|
|
70
93
|
}
|
71
94
|
end
|
72
95
|
|
96
|
+
#Get the global listing for notification schemes.
|
97
|
+
#@return [[JIRA::Scheme]]
|
73
98
|
def get_notification_schemes
|
74
99
|
response = invoke('soap:getNotificationSchemes') { |msg|
|
75
100
|
msg.add 'soap:in0', @auth_token
|
@@ -80,6 +105,9 @@ module RemoteAPI
|
|
80
105
|
}
|
81
106
|
end
|
82
107
|
|
108
|
+
#Get all the versions associated with a project.
|
109
|
+
#@param [String] project_key
|
110
|
+
#@return [[JIRA::Version]]
|
83
111
|
def get_versions_for_project(project_key)
|
84
112
|
response = invoke('soap:getVersions') { |msg|
|
85
113
|
msg.add 'soap:in0', @auth_token
|
@@ -91,6 +119,9 @@ module RemoteAPI
|
|
91
119
|
}
|
92
120
|
end
|
93
121
|
|
122
|
+
#Get the information for a project with a given key.
|
123
|
+
#@param [String] project_key
|
124
|
+
#@return [JIRA::Project]
|
94
125
|
def get_project_with_key(project_key)
|
95
126
|
response = invoke('soap:getProjectByKey') { |msg|
|
96
127
|
msg.add 'soap:in0', @auth_token
|
@@ -100,6 +131,9 @@ module RemoteAPI
|
|
100
131
|
JIRA::Project.project_with_xml_fragment frag
|
101
132
|
end
|
102
133
|
|
134
|
+
#This will only give you basic information about a user.
|
135
|
+
#@param [String] user_name
|
136
|
+
#@return [JIRA::User]
|
103
137
|
def get_user_with_name(user_name)
|
104
138
|
response = invoke('soap:getUser') { |msg|
|
105
139
|
msg.add 'soap:in0', @auth_token
|
@@ -108,6 +142,10 @@ module RemoteAPI
|
|
108
142
|
JIRA::User.user_with_xml_fragment response.document.xpath '//getUserReturn'
|
109
143
|
end
|
110
144
|
|
145
|
+
#Gives you the default avatar image for a project; if you want all
|
146
|
+
#the avatars for a project, use {#get_project_avatars_for_key}.
|
147
|
+
#@param [String] project_key
|
148
|
+
#@return [JIRA::Avatar]
|
111
149
|
def get_project_avatar_for_key(project_key)
|
112
150
|
response = invoke('soap:getProjectAvatar') { |msg|
|
113
151
|
msg.add 'soap:in0', @auth_token
|
@@ -116,6 +154,27 @@ module RemoteAPI
|
|
116
154
|
JIRA::Avatar.avatar_with_xml_fragment response.document.xpath '//getProjectAvatarReturn'
|
117
155
|
end
|
118
156
|
|
157
|
+
#Gives ALL avatars for a given project use this method; if you
|
158
|
+
#just want the default avatar, use {#get_project_avatar_for_key}.
|
159
|
+
#@param [String] project_key
|
160
|
+
#@return [[JIRA::Avatar]]
|
161
|
+
def get_project_avatars_for_key(project_key)
|
162
|
+
response = invoke('soap:getProjectAvatars') { |msg|
|
163
|
+
msg.add 'soap:in0', @auth_token
|
164
|
+
msg.add 'soap:in1', project_key
|
165
|
+
}
|
166
|
+
response.document.xpath("#{RESPONSE_XPATH}/getProjectAvatarsReturn").map {
|
167
|
+
|frag|
|
168
|
+
JIRA::Avatar.avatar_with_xml_fragment frag
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
#This method is the equivalent of making an advanced search from the
|
173
|
+
#web interface.
|
174
|
+
#@param [String] jql_query JQL query as a string
|
175
|
+
#@param [Fixnum] max_results limit on number of returned results;
|
176
|
+
# the value may be overridden by the server if max_results is too large
|
177
|
+
#@return [[JIRA::Issue]]
|
119
178
|
def get_issues_from_jql_search(jql_query, max_results = 500)
|
120
179
|
response = invoke('soap:getIssuesFromJqlSearch') { |msg|
|
121
180
|
msg.add 'soap:in0', @auth_token
|
@@ -128,6 +187,29 @@ module RemoteAPI
|
|
128
187
|
}
|
129
188
|
end
|
130
189
|
|
190
|
+
#This method can update most, but not all, issue fields.
|
191
|
+
#
|
192
|
+
#Fields known to not update via this method:
|
193
|
+
# - status - use {#progress_workflow_action}
|
194
|
+
# - attachments - use {#add_base64_encoded_attachment_to_issue}
|
195
|
+
#
|
196
|
+
#Though JIRA::FieldValue objects have an id field, they do not expect
|
197
|
+
#to be given id values. You must use the name of the field you wish to update.
|
198
|
+
#@example Usage With A Normal Field
|
199
|
+
# summary = JIRA::FieldValue.new
|
200
|
+
# summary.id = 'summary'
|
201
|
+
# summary.values = ['My new summary']
|
202
|
+
#@example Usage With A Custom Field
|
203
|
+
# custom_field = JIRA::FieldValue.new
|
204
|
+
# custom_field.id = 'customfield_10060'
|
205
|
+
# custom_field.values = ['123456']
|
206
|
+
#@example Setting a field to be blank/nil
|
207
|
+
# description = JIRA::FieldValue.field_value_with_nil_values 'description'
|
208
|
+
#@example Calling the method to update an issue
|
209
|
+
# jira_service_instance.update_issue 'PROJECT-1', description, custom_field
|
210
|
+
#@param [String] issue_key
|
211
|
+
#@param [JIRA::FieldValue] *field_values
|
212
|
+
#@return [JIRA::Issue]
|
131
213
|
def update_issue(issue_key, *field_values)
|
132
214
|
response = invoke('soap:updateIssue') { |msg|
|
133
215
|
msg.add 'soap:in0', @auth_token
|
@@ -140,6 +222,16 @@ module RemoteAPI
|
|
140
222
|
JIRA::Issue.issue_with_xml_fragment frag
|
141
223
|
end
|
142
224
|
|
225
|
+
#Some fields will be ignored when an issue is created:
|
226
|
+
# - reporter - you cannot override this value at creation
|
227
|
+
# - due date - I think this is a bug in jiraSOAP or JIRA
|
228
|
+
# - attachments
|
229
|
+
# - votes
|
230
|
+
# - status
|
231
|
+
# - resolution
|
232
|
+
# - environment - I think this is a bug in jiraSOAP or JIRA
|
233
|
+
#@param [JIRA::Issue] issue
|
234
|
+
#@return [JIRA::Issue]
|
143
235
|
def create_issue_with_issue(issue)
|
144
236
|
response = invoke('soap:createIssue') { |msg|
|
145
237
|
msg.add 'soap:in0', @auth_token
|
@@ -175,7 +267,6 @@ end
|
|
175
267
|
# getIssuesFromFilterWithLimit
|
176
268
|
# getIssuesFromTextSearchWithLimit
|
177
269
|
# getIssueTypesForProject
|
178
|
-
# getProjectAvatars
|
179
270
|
# getProjectById
|
180
271
|
# getServerInfo
|
181
272
|
# getSubTaskIssueTypes
|