jiraSOAP 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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