jiraSOAP 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mark Rada
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,101 @@
1
+ jiraSOAP - Ruby interface to the JIRA SOAP API
2
+ ==============================================
3
+
4
+ Uses [handsoap](http://wiki.github.com/unwire/handsoap/) to build a client for the JIRA SOAP API that works on MacRuby as well as Ruby 1.9.
5
+
6
+
7
+ Motivation
8
+ ----------
9
+
10
+ The `jira4r` gem already exists, and works well on Ruby 1.8, but is not compatible with Ruby 1.9 or MacRuby due to its dependance on `soap4r`.
11
+
12
+
13
+ Goals
14
+ -----
15
+
16
+ Pick up where `jira4r` left off:
17
+
18
+ - Implement the current API; `jira4r` does not implement APIs from JIRA 4.x
19
+ - More natural interface; not adhering to the API when the API is weird
20
+ - Speed; network latency is bad enough
21
+
22
+
23
+ Getting Started
24
+ ---------------
25
+
26
+ `jiraSOAP` should run on Ruby 1.9.2 and MacRuby 0.7. Right now you need to build from source, it will be available from gemcutter sometime around the 0.1.0 or 0.2.0 release.
27
+
28
+ git clone git://github.com/Marketcircle/jiraSOAP.git
29
+ rake build
30
+ rake install
31
+
32
+ Once that ugliness is over with, you can run a quick demo (making appropriate substitutions):
33
+
34
+ require 'jiraSOAP'
35
+
36
+ db = JIRA::JIRAService.new 'http://jira.yourSite.com:8080'
37
+ db.login 'user', 'password'
38
+
39
+ issues = db.get_issues_from_jql_search 'reporter = currentUser()', 100
40
+ issues.each { |issue|
41
+ #do something...
42
+ puts issue.key
43
+ }
44
+
45
+ db.logout
46
+
47
+ Get the [Gist](http://gist.github.com/612186).
48
+
49
+
50
+ Notes About Using This Gem
51
+ --------------------------
52
+
53
+ To get a reference for the API, you can look at the JavaDoc stuff provided by Atalssian [here](http://docs.atlassian.com/software/jira/docs/api/rpc-jira-plugin/latest/com/atlassian/jira/rpc/soap/JiraSoapService.html).
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).
67
+
68
+
69
+ TODO
70
+ ----
71
+
72
+ - Performance optimizations; there are a number of places that can be optimized
73
+ + 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
+ - Fix type hacks;. dates should be `NSDate`s and URLs should be `NSURL`s, right now they are all strings
76
+ - Public test suite
77
+ + Needs a mock server
78
+ - Documentation
79
+ - Error handling
80
+ - Finish implementing all of the API
81
+
82
+
83
+ Note on Patches/Pull Requests
84
+ -----------------------------
85
+
86
+ * Fork the project.
87
+ * Make your feature addition or bug fix.
88
+ * Add tests for it. This is important so I don't break it in a
89
+ future version unintentionally.
90
+ * Commit, do not mess with rakefile, version, or history.
91
+ (if you want to have your own version, that is fine but
92
+ bump version in a commit by itself I can ignore when I pull)
93
+ * Send me a pull request. Bonus points for topic branches.
94
+
95
+
96
+ License
97
+ -------
98
+
99
+ Copyright: [Marketcircle Inc.](http://www.marketcircle.com/), 2010
100
+
101
+ See LICENSE for details.
data/lib/jiraSOAP.rb ADDED
@@ -0,0 +1,12 @@
1
+ #TODO: set a requirement on the handsoap version
2
+ require 'handsoap'
3
+ require 'logger'
4
+
5
+ require 'jiraSOAP/handsoap_extensions.rb'
6
+ require 'jiraSOAP/remoteEntities.rb'
7
+ require 'jiraSOAP/remoteAPI.rb'
8
+
9
+ require 'jiraSOAP/JIRAservice.rb'
10
+
11
+ #overrides and additions
12
+ require 'lib/macruby_stuff.rb' if RUBY_ENGINE == 'macruby'
@@ -0,0 +1,38 @@
1
+ module JIRA
2
+ class JIRAService < Handsoap::Service
3
+ include RemoteAPI
4
+
5
+ attr_reader :auth_token, :user
6
+
7
+ def self.instance_at_url(url, user, password)
8
+ jira = JIRAService.new url
9
+ jira.login user, password
10
+ jira
11
+ end
12
+
13
+ def initialize(endpoint_url)
14
+ super
15
+
16
+ @endpoint_url = endpoint_url
17
+ endpoint_data = {
18
+ :uri => "#{endpoint_url}/rpc/soap/jirasoapservice-v2",
19
+ :version => 2
20
+ }
21
+ self.class.endpoint endpoint_data
22
+ end
23
+
24
+ #PONDER: a finalizer that will try to logout
25
+
26
+ def method_missing(method, *args)
27
+ $stderr.puts "#{method} is not a defined method in the API...yet"
28
+ end
29
+
30
+ protected
31
+ def on_create_document(doc)
32
+ doc.alias 'soap', 'http://soap.rpc.jira.atlassian.com'
33
+ end
34
+ def on_response_document(doc)
35
+ doc.add_namespace 'jir', @endpoint_url
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ #PONDER: send upstream for consideration
2
+ module Handsoap
3
+ module XmlMason
4
+ class Node
5
+ #TODO: make recursive
6
+ def add_simple_array(node_name, array = [], options = {})
7
+ prefix, name = parse_ns(node_name)
8
+ node = append_child Element.new(self, prefix, name, nil, options)
9
+ array.each { |element| node.add node_name, element }
10
+ end
11
+
12
+ def add_complex_array(node_name, array = [], options = {})
13
+ prefix, name = parse_ns(node_name)
14
+ node = append_child Element.new(self, prefix, name, nil, options)
15
+ array.each { |element| element.soapify_for node, name }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ Handsoap.http_driver = :net_http #curb does not build for MacRuby
2
+
3
+ module JIRA
4
+ #overrides for MacRuby
5
+
6
+ end
@@ -0,0 +1,189 @@
1
+ module RemoteAPI
2
+ RESPONSE_XPATH = '/node()[1]/node()[1]/node()[1]/node()[2]'
3
+
4
+ def login(user, password)
5
+ response = invoke('soap:login') { |msg|
6
+ msg.add 'soap:in0', user
7
+ msg.add 'soap:in1', password
8
+ }
9
+ #TODO: error handling (catch the exception and look at the Response node?)
10
+ #cache now that we know it is safe to do so
11
+ @user = user
12
+ @auth_token = response.document.xpath('//loginReturn').first.to_s
13
+ true
14
+ end
15
+
16
+ def logout
17
+ response = invoke('soap:logout') { |msg|
18
+ msg.add 'soap:in0', @auth_token
19
+ }
20
+ response.document.xpath('//logoutReturn').first.to_s == 'true'
21
+ end
22
+
23
+ def get_priorities
24
+ response = invoke('soap:getPriorities') { |msg|
25
+ msg.add 'soap:in0', @auth_token
26
+ }
27
+ response.document.xpath("#{RESPONSE_XPATH}/getPrioritiesReturn").map {
28
+ |frag|
29
+ JIRA::Priority.priority_with_xml_fragment frag
30
+ }
31
+ end
32
+
33
+ def get_resolutions
34
+ response = invoke('soap:getResolutions') { |msg|
35
+ msg.add 'soap:in0', @auth_token
36
+ }
37
+ response.document.xpath("#{RESPONSE_XPATH}/getResolutionsReturn").map {
38
+ |frag|
39
+ JIRA::Resolution.resolution_with_xml_fragment frag
40
+ }
41
+ end
42
+
43
+ def get_custom_fields
44
+ response = invoke('soap:getCustomFields') { |msg|
45
+ msg.add 'soap:in0', @auth_token
46
+ }
47
+ response.document.xpath("#{RESPONSE_XPATH}/getCustomFieldsReturn").map {
48
+ |frag|
49
+ JIRA::Field.field_with_xml_fragment frag
50
+ }
51
+ end
52
+
53
+ def get_issue_types
54
+ response = invoke('soap:getIssueTypes') { |msg|
55
+ msg.add 'soap:in0', @auth_token
56
+ }
57
+ response.document.xpath("#{RESPONSE_XPATH}/getIssueTypesReturn").map {
58
+ |frag|
59
+ JIRA::IssueType.issue_type_with_xml_fragment frag
60
+ }
61
+ end
62
+
63
+ def get_statuses
64
+ response = invoke('soap:getStatuses') { |msg|
65
+ msg.add 'soap:in0', @auth_token
66
+ }
67
+ response.document.xpath("#{RESPONSE_XPATH}/getStatusesReturn").map {
68
+ |frag|
69
+ JIRA::Status.status_with_xml_fragment frag
70
+ }
71
+ end
72
+
73
+ def get_notification_schemes
74
+ response = invoke('soap:getNotificationSchemes') { |msg|
75
+ msg.add 'soap:in0', @auth_token
76
+ }
77
+ response.document.xpath("#{RESPONSE_XPATH}/getNotificationSchemesReturn").map {
78
+ |frag|
79
+ JIRA::Scheme.scheme_with_xml_fragment frag
80
+ }
81
+ end
82
+
83
+ def get_versions_for_project(project_key)
84
+ response = invoke('soap:getVersions') { |msg|
85
+ msg.add 'soap:in0', @auth_token
86
+ msg.add 'soap:in1', project_key
87
+ }
88
+ response.document.xpath("#{RESPONSE_XPATH}/getVersionsReturn").map {
89
+ |frag|
90
+ JIRA::Version.version_with_xml_fragment frag
91
+ }
92
+ end
93
+
94
+ def get_project_with_key(project_key)
95
+ response = invoke('soap:getProjectByKey') { |msg|
96
+ msg.add 'soap:in0', @auth_token
97
+ msg.add 'soap:in1', project_key
98
+ }
99
+ frag = response.document.xpath '//getProjectByKeyReturn'
100
+ JIRA::Project.project_with_xml_fragment frag
101
+ end
102
+
103
+ def get_user_with_name(user_name)
104
+ response = invoke('soap:getUser') { |msg|
105
+ msg.add 'soap:in0', @auth_token
106
+ msg.add 'soap:in1', user_name
107
+ }
108
+ JIRA::User.user_with_xml_fragment response.document.xpath '//getUserReturn'
109
+ end
110
+
111
+ def get_project_avatar_for_key(project_key)
112
+ response = invoke('soap:getProjectAvatar') { |msg|
113
+ msg.add 'soap:in0', @auth_token
114
+ msg.add 'soap:in1', project_key
115
+ }
116
+ JIRA::Avatar.avatar_with_xml_fragment response.document.xpath '//getProjectAvatarReturn'
117
+ end
118
+
119
+ def get_issues_from_jql_search(jql_query, max_results = 500)
120
+ response = invoke('soap:getIssuesFromJqlSearch') { |msg|
121
+ msg.add 'soap:in0', @auth_token
122
+ msg.add 'soap:in1', jql_query
123
+ msg.add 'soap:in2', max_results
124
+ }
125
+ response.document.xpath("#{RESPONSE_XPATH}/getIssuesFromJqlSearchReturn").map {
126
+ |frag|
127
+ JIRA::Issue.issue_with_xml_fragment frag
128
+ }
129
+ end
130
+
131
+ def update_issue(issue_key, *field_values)
132
+ response = invoke('soap:updateIssue') { |msg|
133
+ msg.add 'soap:in0', @auth_token
134
+ msg.add 'soap:in1', issue_key
135
+ msg.add 'soap:in2' do |submsg|
136
+ field_values.each { |fv| fv.soapify_for submsg }
137
+ end
138
+ }
139
+ frag = response.document.xpath '//updateIssueReturn'
140
+ JIRA::Issue.issue_with_xml_fragment frag
141
+ end
142
+
143
+ def create_issue_with_issue(issue)
144
+ response = invoke('soap:createIssue') { |msg|
145
+ msg.add 'soap:in0', @auth_token
146
+ msg.add 'soap:in1' do |submsg|
147
+ issue.soapify_for submsg
148
+ end
149
+ }
150
+ frag = response.document.xpath '//createIssueReturn'
151
+ JIRA::Issue.issue_with_xml_fragment frag
152
+ end
153
+ end
154
+
155
+ #TODO: next block of useful methods
156
+ # addBase64EncodedAttachmentsToIssue
157
+ # addComment
158
+ # addVersion
159
+ # archiveVersion
160
+ # createProject
161
+ # createProjectRole
162
+ # createUser
163
+ # deleteProjectAvatar
164
+ # deleteUser
165
+ # editComment
166
+ # getAttachmentsFromIssue
167
+ # getAvailableActions
168
+ # getComment
169
+ # getComments
170
+ # getComponents
171
+ # getFavouriteFilters
172
+ # getIssue
173
+ # getIssueById
174
+ # getIssueCountForFilter
175
+ # getIssuesFromFilterWithLimit
176
+ # getIssuesFromTextSearchWithLimit
177
+ # getIssueTypesForProject
178
+ # getProjectAvatars
179
+ # getProjectById
180
+ # getServerInfo
181
+ # getSubTaskIssueTypes
182
+ # getSubTaskIssueTypesForProject
183
+ # progressWorkflowAction
184
+ # refreshCustomFields
185
+ # releaseVersion
186
+ # setProjectAvatar (change to different existing)
187
+ # setNewProjectAvatar (upload new and set it)
188
+ # updateProject
189
+ # progressWorkflowAction
@@ -0,0 +1,297 @@
1
+ module JIRA
2
+
3
+ class Priority
4
+ attr_accessor :id, :name, :color, :icon, :description
5
+ def self.priority_with_xml_fragment(frag)
6
+ return if frag.nil?
7
+ priority = Priority.new
8
+ priority.id = frag.xpath('id').to_s
9
+ priority.name = frag.xpath('name').to_s
10
+ priority.color = frag.xpath('color').to_s #PONDER: hex
11
+ priority.icon = frag.xpath('icon').to_s #FIXME: NSURL
12
+ priority.description = frag.xpath('description').to_s
13
+ priority
14
+ end
15
+ end
16
+
17
+ class Resolution
18
+ attr_accessor :id, :name, :icon, :description
19
+ def self.resolution_with_xml_fragment(frag)
20
+ return if frag.nil?
21
+ resolution = Resolution.new
22
+ resolution.id = frag.xpath('id').to_s
23
+ resolution.name = frag.xpath('name').to_s
24
+ resolution.icon = frag.xpath('icon').to_s #FIXME: NSURL
25
+ resolution.description = frag.xpath('description').to_s
26
+ resolution
27
+ end
28
+ end
29
+
30
+ class Field
31
+ attr_accessor :id, :name
32
+ def self.field_with_xml_fragment(frag)
33
+ return if frag.nil?
34
+ field = Field.new
35
+ field.id = frag.xpath('id').to_s
36
+ field.name = frag.xpath('name').to_s
37
+ field
38
+ end
39
+ end
40
+
41
+ class CustomField
42
+ attr_accessor :id, :key, :values
43
+ def self.custom_field_with_xml_fragment(frag)
44
+ return if frag.nil?
45
+ custom_field = CustomField.new
46
+ custom_field.id = frag.xpath('customfieldId').to_s
47
+ custom_field.key = frag.xpath('key').to_s
48
+ custom_field.values = frag.xpath('values/*').map { |value| value.to_s }
49
+ custom_field
50
+ end
51
+ def soapify_for(msg, label = 'customFieldValues')
52
+ msg.add label do |submsg|
53
+ submsg.add 'customfieldId', @id
54
+ submsg.add 'key', @key #TODO: see if this is always nil
55
+ submsg.add_simple_array 'values', @values
56
+ end
57
+ end
58
+ end
59
+
60
+ class IssueType
61
+ attr_accessor :id, :name, :icon, :description
62
+ attr_writer :subtask
63
+ def subtask?; @subtask; end
64
+ def self.issue_type_with_xml_fragment(frag)
65
+ return if frag.nil?
66
+ issue_type = IssueType.new
67
+ issue_type.id = frag.xpath('id').to_s
68
+ issue_type.name = frag.xpath('name').to_s
69
+ issue_type.icon = frag.xpath('icon').to_s #FIXME: NSURL
70
+ issue_type.subtask = frag.xpath('subtask').to_s == 'true'
71
+ issue_type.description = frag.xpath('description').to_s
72
+ issue_type
73
+ end
74
+ end
75
+
76
+ class Status
77
+ attr_accessor :id, :name, :icon, :description
78
+ def self.status_with_xml_fragment(frag)
79
+ return if frag.nil?
80
+ status = Status.new
81
+ status.id = frag.xpath('id').to_s
82
+ status.name = frag.xpath('name').to_s
83
+ status.icon = frag.xpath('icon').to_s #FIXME: NSURL
84
+ status.description = frag.xpath('description').to_s
85
+ status
86
+ end
87
+ end
88
+
89
+ class Version
90
+ attr_accessor :id, :name, :sequence, :released, :archived, :release_date
91
+ attr_writer :released, :archived
92
+ def released?; @released; end
93
+ def archived?; @archived; end
94
+ def self.version_with_xml_fragment(frag)
95
+ return if frag.nil?
96
+ #TODO: find out why we don't get a description for this type
97
+ version = Version.new
98
+ version.id = frag.xpath('id').to_s
99
+ version.name = frag.xpath('name').to_s
100
+ version.sequence = frag.xpath('sequence').to_s.to_i
101
+ version.released = frag.xpath('released').to_s == 'true'
102
+ version.archived = frag.xpath('archived').to_s == 'true'
103
+ version.release_date = frag.xpath('releaseDate').to_s #FIXME: NSDate
104
+ version
105
+ end
106
+ end
107
+
108
+ class Scheme
109
+ attr_accessor :id, :name, :type, :description
110
+ def self.scheme_with_xml_fragment(frag)
111
+ return if frag.nil?
112
+ scheme = Scheme.new
113
+ scheme.id = frag.xpath('id').to_s
114
+ scheme.name = frag.xpath('name').to_s
115
+ scheme.type = frag.xpath('type').to_s
116
+ scheme.description = frag.xpath('description').to_s
117
+ scheme
118
+ end
119
+ end
120
+
121
+ class Component
122
+ attr_accessor :id, :name
123
+ def self.component_with_xml_fragment(frag)
124
+ return if frag.nil?
125
+ component = Component.new
126
+ component.id = frag.xpath('id').to_s
127
+ component.name = frag.xpath('name').to_s
128
+ component
129
+ end
130
+ end
131
+
132
+ class Project
133
+ attr_accessor :id, :name, :key, :url, :project_url, :lead, :description
134
+ attr_accessor :issue_security_scheme, :notification_scheme, :permission_scheme
135
+ def self.project_with_xml_fragment(frag)
136
+ return if frag.nil?
137
+ project = Project.new
138
+ project.id = frag.xpath('id').to_s
139
+ project.name = frag.xpath('name').to_s
140
+ project.key = frag.xpath('key').to_s
141
+ project.url = frag.xpath('url').to_s #FIXME: NSURL
142
+ project.project_url = frag.xpath('projectUrl').to_s #FIXME: NSURL
143
+ project.lead = frag.xpath('lead').to_s
144
+ project.description = frag.xpath('description').to_s
145
+ #TODO: find out why the server always seems to pass nil
146
+ project.issue_security_scheme =
147
+ Scheme.scheme_with_xml_fragment frag.xpath 'issueSecurityScheme'
148
+ project.notification_scheme =
149
+ Scheme.scheme_with_xml_fragment frag.xpath 'notificationScheme'
150
+ project.permission_scheme =
151
+ Scheme.scheme_with_xml_fragment frag.xpath 'permissionScheme'
152
+ project
153
+ end
154
+ end
155
+
156
+ class Avatar
157
+ attr_accessor :id, :owner, :type, :content_type, :base64_data
158
+ attr_writer :system
159
+ def system?; @system; end
160
+ def self.avatar_with_xml_fragment(frag)
161
+ return if frag.nil?
162
+ avatar = Avatar.new
163
+ avatar.id = frag.xpath('id').to_s
164
+ avatar.owner = frag.xpath('owner').to_s
165
+ avatar.system = frag.xpath('system').to_s == 'true'
166
+ avatar.type = frag.xpath('type').to_s
167
+ avatar.content_type = frag.xpath('contentType').to_s
168
+ avatar.base64_data = frag.xpath('base64Data').to_s
169
+ avatar
170
+ end
171
+ end
172
+
173
+ #easily the most convoluted structure in the API
174
+ #will most likely be the greatest source of bugs
175
+ class Issue
176
+ attr_accessor :id, :key, :summary, :description, :type_id, :last_updated
177
+ attr_accessor :votes, :status_id, :assignee_name, :reporter_name, :priority_id
178
+ attr_accessor :project_name, :affects_versions, :create_date, :due_date
179
+ attr_accessor :fix_versions, :resolution_id, :environment, :components
180
+ attr_accessor :attachment_names, :custom_field_values
181
+ def self.issue_with_xml_fragment(frag)
182
+ return if frag.nil?
183
+ issue = Issue.new
184
+ issue.affects_versions = frag.xpath('affectsVersions/*').map { |frag|
185
+ Version.version_with_xml_fragment frag
186
+ }
187
+ issue.fix_versions = frag.xpath('fixVersions/*').map { |frag|
188
+ Version.version_with_xml_fragment frag
189
+ }
190
+ issue.components = frag.xpath('components/*').map { |frag|
191
+ Component.component_with_xml_fragment frag
192
+ }
193
+ issue.custom_field_values = frag.xpath('customFieldValues/*').map { |frag|
194
+ CustomField.custom_field_with_xml_fragment frag
195
+ }
196
+ issue.attachment_names = frag.xpath('attachmentNames/*').map { |name|
197
+ name.to_s
198
+ }
199
+ issue.id = frag.xpath('id').to_s
200
+ issue.key = frag.xpath('key').to_s
201
+ issue.summary = frag.xpath('summary').to_s
202
+ issue.description = frag.xpath('description').to_s
203
+ issue.type_id = frag.xpath('type').to_s
204
+ issue.last_updated = frag.xpath('updated').to_s
205
+ issue.votes = frag.xpath('votes').to_s.to_i
206
+ issue.status_id = frag.xpath('status').to_s
207
+ issue.assignee_name = frag.xpath('assignee').to_s
208
+ issue.reporter_name = frag.xpath('reporter').to_s
209
+ issue.priority_id = frag.xpath('priority').to_s
210
+ issue.project_name = frag.xpath('project').to_s
211
+ issue.create_date = frag.xpath('created').to_s #FIXME: NSDate
212
+ issue.due_date = frag.xpath('duedate').to_s #FIXME: NSDate
213
+ issue.resolution_id = frag.xpath('resolution').to_s
214
+ issue.environment = frag.xpath('environment').to_s
215
+ issue
216
+ 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
+
226
+ #might be going away, since it appears to have no effect at creation time
227
+ msg.add 'reporter', @reporter_name unless @reporter.nil?
228
+
229
+ msg.add 'priority', @priority_id
230
+ msg.add 'type', @type_id
231
+ msg.add 'project', @project_name
232
+
233
+ msg.add 'summary', @summary
234
+ msg.add 'description', @description
235
+
236
+ #server only accepts issues if components/versions are just ids
237
+ msg.add 'components' do |submsg|
238
+ (@components || []).each { |component|
239
+ submsg.add 'components' do |component_msg|
240
+ component_msg.add 'id', component.id
241
+ end
242
+ }
243
+ end
244
+ msg.add 'affectsVersions' do |submsg|
245
+ (@affects_versions || []).each { |version|
246
+ submsg.add 'affectsVersions' do |version_msg|
247
+ version_msg.add 'id', version.id
248
+ end
249
+ }
250
+ end
251
+ msg.add 'fixVersions' do |submsg|
252
+ (@fix_versions || []).each { |version|
253
+ submsg.add 'fixVersions' do |version_msg|
254
+ version_msg.add 'id', version.id end
255
+ }
256
+ end
257
+
258
+ #-1 is the value you send to get the automatic assignee
259
+ msg.add 'assignee', (@assignee_name || '-1')
260
+
261
+ msg.add_complex_array 'customFieldValues', (@custom_field_values || [])
262
+
263
+ #passing environment/due_date when nil seems to mess up the server
264
+ msg.add 'environment', @environment unless @environment.nil?
265
+ msg.add 'duedate', @due_date unless @due_date.nil?
266
+ end
267
+ end
268
+
269
+ class User
270
+ attr_accessor :name, :full_name, :email
271
+ def self.user_with_xml_fragment(frag)
272
+ return if frag.nil?
273
+ user = User.new
274
+ user.name = frag.xpath('name').to_s
275
+ user.full_name = frag.xpath('fullname').to_s
276
+ user.email = frag.xpath('email').to_s
277
+ user
278
+ end
279
+ end
280
+
281
+ class FieldValue
282
+ attr_accessor :id, :values
283
+ def self.field_value_with_nil_values(id)
284
+ fv = FieldValue.new
285
+ fv.id = id
286
+ fv.values = [nil]
287
+ fv
288
+ end
289
+ def soapify_for(message, label = 'fieldValue')
290
+ message.add label do |message|
291
+ message.add 'id', @id
292
+ message.add_simple_array 'values', @values
293
+ end
294
+ end
295
+ end
296
+
297
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class JirasoapTest < Mini::Test::TestCase
4
+ def test_something_for_real
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'mini/test'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'jiraSOAP'
7
+
8
+ class Mini::Test::TestCase
9
+ end
10
+
11
+ Mini::Test.autorun
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jiraSOAP
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Mark Rada
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-06 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: handsoap
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 1
31
+ version: "1.1"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: minitest
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ version_requirements: *id002
47
+ description: Written to run fast and work on Ruby 1.9 as well as MacRuby
48
+ email: mrada@marketcircle.com
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ extra_rdoc_files:
54
+ - LICENSE
55
+ - README.markdown
56
+ files:
57
+ - lib/jiraSOAP.rb
58
+ - lib/jiraSOAP/JIRAservice.rb
59
+ - lib/jiraSOAP/handsoap_extensions.rb
60
+ - lib/jiraSOAP/macruby_stuff.rb
61
+ - lib/jiraSOAP/remoteAPI.rb
62
+ - lib/jiraSOAP/remoteEntities.rb
63
+ - LICENSE
64
+ - README.markdown
65
+ - test/jiraSOAP_test.rb
66
+ - test/test_helper.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/ferrous26/jiraSOAP
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 1
83
+ - 9
84
+ - 2
85
+ version: 1.9.2
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.7
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: A Ruby client for the JIRA SOAP API
101
+ test_files:
102
+ - test/jiraSOAP_test.rb
103
+ - test/test_helper.rb