ruby_bugzilla 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,10 @@
1
1
  require 'linux_admin'
2
+ require "xmlrpc/client"
2
3
 
3
4
  class RubyBugzilla
5
+ CLONE_FIELDS = [:assigned_to, :cc, :cf_devel_whiteboard, :cf_internal_whiteboard, :component,
6
+ :groups, :keywords, :op_sys, :platform, :priority, :product, :qa_contact, :severity,
7
+ :summary, :target_release, :url, :version, :whiteboard, :comments, :description,]
4
8
  CMD = `which bugzilla`.chomp
5
9
  COOKIES_FILE = File.expand_path('~/.bugzillacookies')
6
10
 
@@ -16,11 +20,12 @@ class RubyBugzilla
16
20
  File.delete(COOKIES_FILE) if File.exists?(COOKIES_FILE)
17
21
  end
18
22
 
19
- attr_accessor :bugzilla_uri, :username, :password, :last_command
20
- attr_reader :bugzilla_request_uri
23
+ attr_accessor :bugzilla_uri, :username, :password, :last_command, :xmlrpc
24
+ attr_reader :bugzilla_request_uri, :bugzilla_request_hostname
21
25
 
22
26
  def bugzilla_uri=(value)
23
27
  @bugzilla_request_uri = URI.join(value, "xmlrpc.cgi").to_s
28
+ @bugzilla_request_hostname = URI(value).hostname
24
29
  @bugzilla_uri = value
25
30
  end
26
31
 
@@ -31,6 +36,8 @@ class RubyBugzilla
31
36
  self.bugzilla_uri = bugzilla_uri
32
37
  self.username = username
33
38
  self.password = password
39
+ self.xmlrpc = ::XMLRPC::Client.new(bugzilla_request_hostname, '/xmlrpc.cgi', 443, nil,
40
+ nil, username, password, true, 60)
34
41
  end
35
42
 
36
43
  def inspect
@@ -60,7 +67,7 @@ class RubyBugzilla
60
67
  params["login"] = [username, password]
61
68
 
62
69
  begin
63
- execute(params)
70
+ execute_shell(params)
64
71
  rescue
65
72
  clear_login! # A failed login attempt could result in a corrupt COOKIES_FILE
66
73
  raise
@@ -93,7 +100,7 @@ class RubyBugzilla
93
100
  params["query"] = nil
94
101
  set_params_options(params, options)
95
102
 
96
- execute(params)
103
+ execute_shell(params)
97
104
  end
98
105
 
99
106
  # Modify an existing bug or set of bugs
@@ -123,16 +130,83 @@ class RubyBugzilla
123
130
  params["modify"] = bug_ids
124
131
  set_params_options(params, options)
125
132
 
126
- execute(params)
133
+ execute_shell(params)
134
+ end
135
+
136
+ # Clone of an existing bug
137
+ #
138
+ # Example:
139
+ # # Perform a clone of an existing bug, and return the new bug ID.
140
+ # bz.clone(948970)
141
+ #
142
+ # @param bug_id [String, Fixnum] A single bug id to process.
143
+ # @param overrides [Hash] The properties to change from the source bug. Some properties include
144
+ # * <tt>:target_release</tt> - The target release for the new cloned bug.
145
+ # * <tt>:assigned_to</tt> - The person to assign the new cloned bug to.
146
+ # @return [Fixnum] The bug id to the new, cloned, bug.
147
+ def clone(bug_id, overrides={})
148
+ raise ArgumentError, "bug_id must be numeric" unless bug_id.to_s =~ /^\d+$/
149
+
150
+ existing_bz = xmlrpc_bug_query(bug_id)
151
+
152
+ clone_description, clone_comment_is_private = assemble_clone_description(existing_bz)
153
+
154
+ params = {}
155
+ CLONE_FIELDS.each do |field|
156
+ next if field == :comments
157
+ params[field] = existing_bz[field.to_s]
158
+ end
159
+
160
+ # Apply overrides
161
+ overrides.each do |param, value|
162
+ params[param] = value
163
+ end
164
+
165
+ # Apply base clone fields
166
+ params[:cf_clone_of] = bug_id
167
+ params[:description] = clone_description
168
+ params[:comment_is_private] = clone_comment_is_private
169
+
170
+ execute_xmlrpc('create', params)[:id.to_s]
171
+ end
172
+
173
+ # XMLRPC Bug Query of an existing bug
174
+ #
175
+ # Example:
176
+ # # Perform an xmlrpc query for a single bug.
177
+ # bz.xmlrpc_bug_query(948970)
178
+ #
179
+ # @param bug_id [String, Fixnum] A single bug id to process.
180
+ # @return [Fixnum] The bug id to the new, cloned, bug.
181
+ def xmlrpc_bug_query(bug_id)
182
+ raise ArgumentError, "bug_id must be numeric" unless bug_id.to_s =~ /^\d+$/
183
+
184
+ params = {}
185
+ params[:Bugzilla_login] = username
186
+ params[:Bugzilla_password] = password
187
+ params[:ids] = bug_id
188
+ params[:include_fields] = CLONE_FIELDS
189
+
190
+ execute_xmlrpc('get', params)['bugs'].last
127
191
  end
128
192
 
129
193
  private
194
+ def assemble_clone_description(existing_bz)
195
+ clone_description = " +++ This bug was initially created as a clone of Bug ##{existing_bz[:id.to_s]} +++ \n"
196
+ clone_description << existing_bz[:description.to_s]
130
197
 
131
- def execute(params)
132
- params = {"--bugzilla=" => bugzilla_request_uri}.merge(params)
198
+ clone_comment_is_private = false
199
+ existing_bz[:comments.to_s].each do |comment|
200
+ clone_description << "\n\n"
201
+ clone_description << "*" * 70
202
+ clone_description << "\nFollowing comment by %s on %s\n\n" %
203
+ [comment['author'], comment['creation_time'].to_time]
204
+ clone_description << "\n\n"
205
+ clone_description << comment['text']
206
+ clone_comment_is_private = true if comment['is_private']
207
+ end
133
208
 
134
- self.last_command = string_command(CMD, params, password)
135
- LinuxAdmin.run!(CMD, :params => params).output
209
+ [clone_description, clone_comment_is_private]
136
210
  end
137
211
 
138
212
  def set_params_options(params, options)
@@ -141,7 +215,24 @@ class RubyBugzilla
141
215
  end
142
216
  end
143
217
 
144
- def string_command(cmd, params = {}, password=nil)
218
+ # Execute the command using LinuxAdmin to execute python-bugzilla shell commands.
219
+ def execute_shell(params)
220
+ params = {"--bugzilla=" => bugzilla_request_uri}.merge(params)
221
+
222
+ self.last_command = shell_command_string(CMD, params, password)
223
+ LinuxAdmin.run!(CMD, :params => params).output
224
+ end
225
+
226
+ # Bypass python-bugzilla and use the xmlrpc API directly.
227
+ def execute_xmlrpc(action, params)
228
+ cmd = "Bug.#{action}"
229
+
230
+ self.last_command = xmlrpc_command_string(cmd, params)
231
+ xmlrpc.call(cmd, params)
232
+ end
233
+
234
+ # Build a printable representation of the python-bugzilla command executed.
235
+ def shell_command_string(cmd, params = {}, password=nil)
145
236
  scrubbed_str = str = ""
146
237
  str << cmd
147
238
  params.each do |param, value|
@@ -158,4 +249,12 @@ class RubyBugzilla
158
249
  scrubbed_str = str.sub(password, "********") unless password.nil?
159
250
  scrubbed_str
160
251
  end
252
+
253
+ # Build a printable representation of the xmlrcp command executed.
254
+ def xmlrpc_command_string(cmd, params = {})
255
+ clean_params = Hash[params]
256
+ clean_params[:Bugzilla_password] = "********"
257
+ "xmlrpc.call(#{cmd}, #{clean_params})"
258
+ end
259
+
161
260
  end
@@ -1,3 +1,3 @@
1
1
  class RubyBugzilla
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -124,4 +124,147 @@ describe RubyBugzilla do
124
124
  bz.last_command.should include("948972")
125
125
  end
126
126
  end
127
+
128
+ context "#xmlrpc_bug_query" do
129
+ it "when no argument is specified" do
130
+ expect { bz.xmlrpc_bug_query }.to raise_error(ArgumentError)
131
+ end
132
+
133
+ it "when an invalid argument is specified" do
134
+ expect { bz.xmlrpc_bug_query("not a Fixnum") }.to raise_error(ArgumentError)
135
+ end
136
+
137
+ it "when the specified bug does not exist" do
138
+ output = {}
139
+
140
+ allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
141
+ expect { bz.xmlrpc_bug_query(94897099) }.to raise_error
142
+ end
143
+
144
+ it "when producing valid output" do
145
+ output = {
146
+ 'bugs' => [
147
+ {
148
+ "priority" => "unspecified",
149
+ "keywords" => ["ZStream"],
150
+ "cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
151
+ },
152
+ ]
153
+ }
154
+
155
+ allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
156
+ existing_bz = bz.xmlrpc_bug_query("948972")
157
+
158
+
159
+ bz.last_command.should include("Bug.get")
160
+
161
+ existing_bz["priority"].should == "unspecified"
162
+ existing_bz["keywords"].should == ["ZStream"]
163
+ existing_bz["cc"].should == ["calvin@redhat.com", "hobbes@RedHat.com"]
164
+
165
+ end
166
+ end
167
+
168
+ context "#clone" do
169
+ it "when no argument is specified" do
170
+ expect { bz.clone }.to raise_error(ArgumentError)
171
+ end
172
+
173
+ it "when an invalid argument is specified" do
174
+ expect { bz.clone("not a Fixnum") }.to raise_error(ArgumentError)
175
+ end
176
+
177
+ it "when the specified bug to clone does not exist" do
178
+ output = {}
179
+
180
+ allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
181
+ expect { bz.clone(94897099) }.to raise_error
182
+ end
183
+
184
+ it "when producing valid output" do
185
+ output = {"id" => 948992}
186
+ existing_bz = {
187
+ "description" => "Description of problem:\n\nIt's Broken",
188
+ "priority" => "unspecified",
189
+ "assigned_to" => "calvin@redhat.com",
190
+ "target_release" => ["---"],
191
+ "keywords" => ["ZStream"],
192
+ "cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
193
+ "comments" => [
194
+ {
195
+ "is_private" => false,
196
+ "count" => 0,
197
+ "time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
198
+ "bug_id" => 948970,
199
+ "author" => "Calvin@redhat.com",
200
+ "text" => "It's Broken and impossible to reproduce",
201
+ "creation_time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
202
+ "id" => 5777871,
203
+ "creator_id" => 349490
204
+ },
205
+ {
206
+ "is_private" => false,
207
+ "count" => 1,
208
+ "time" => XMLRPC::DateTime.new(1970, 11, 10, 16, 18, 30),
209
+ "bug_id" => 948970,
210
+ "author" => "Hobbes@redhat.com",
211
+ "text" => "Fix Me Now!",
212
+ "creation_time" => XMLRPC::DateTime.new(1972, 2, 14, 0, 0, 0),
213
+ "id" => 5782170,
214
+ "creator_id" => 349490
215
+ },]
216
+ }
217
+
218
+ RubyBugzilla.any_instance.stub(:xmlrpc_bug_query).and_return(existing_bz)
219
+ allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_create', :call => output))
220
+ new_bz_id = bz.clone("948972")
221
+
222
+ bz.last_command.should include("Bug.create")
223
+
224
+ new_bz_id.should == output["id"]
225
+ end
226
+
227
+ it "when providing override values" do
228
+ output = {"id" => 948992}
229
+ existing_bz = {
230
+ "description" => "Description of problem:\n\nIt's Broken",
231
+ "priority" => "unspecified",
232
+ "assigned_to" => "calvin@redhat.com",
233
+ "target_release" => ["---"],
234
+ "keywords" => ["ZStream"],
235
+ "cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
236
+ "comments" => [
237
+ {
238
+ "is_private" => false,
239
+ "count" => 0,
240
+ "time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
241
+ "bug_id" => 948970,
242
+ "author" => "Buzz.Aldrin@redhat.com",
243
+ "text" => "It's Broken and impossible to reproduce",
244
+ "creation_time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
245
+ "id" => 5777871,
246
+ "creator_id" => 349490
247
+ },
248
+ {
249
+ "is_private" => false,
250
+ "count" => 1,
251
+ "time" => XMLRPC::DateTime.new(1970, 11, 10, 16, 18, 30),
252
+ "bug_id" => 948970,
253
+ "author" => "Neil.Armstrong@redhat.com",
254
+ "text" => "Fix Me Now!",
255
+ "creation_time" => XMLRPC::DateTime.new(1972, 2, 14, 0, 0, 0),
256
+ "id" => 5782170,
257
+ "creator_id" => 349490
258
+ },]
259
+ }
260
+
261
+ RubyBugzilla.any_instance.stub(:xmlrpc_bug_query).and_return(existing_bz)
262
+ allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_create', :call => output))
263
+ new_bz_id = bz.clone("948972", {"assigned_to" => "Ham@NASA.gov", "target_release" => ["2.2.0"],} )
264
+
265
+ bz.last_command.should include("Bug.create")
266
+
267
+ new_bz_id.should == output["id"]
268
+ end
269
+ end
127
270
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_bugzilla
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-12-12 00:00:00.000000000 Z
13
+ date: 2014-01-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -125,21 +125,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
125
  - - ! '>='
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
- segments:
129
- - 0
130
- hash: 3744538861231322525
131
128
  required_rubygems_version: !ruby/object:Gem::Requirement
132
129
  none: false
133
130
  requirements:
134
131
  - - ! '>='
135
132
  - !ruby/object:Gem::Version
136
133
  version: '0'
137
- segments:
138
- - 0
139
- hash: 3744538861231322525
140
134
  requirements: []
141
135
  rubyforge_project:
142
- rubygems_version: 1.8.23
136
+ rubygems_version: 1.8.25
143
137
  signing_key:
144
138
  specification_version: 3
145
139
  summary: RubyBugzilla is a Ruby wrapper around the python-bugzilla CLI for easy access