line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# $Id: Bugzilla3.pm 28 2008-10-07 10:09:30Z swined $ |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package WWW::Bugzilla3; |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
37923
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
69
|
|
8
|
2
|
|
|
2
|
|
10
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
86
|
|
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
215
|
|
11
|
2
|
|
|
2
|
|
810
|
use RPC::XML::Client; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
use URI::Escape; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.71'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 NAME |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
WWW::Bugzilla3 - perl bindings for Bugzilla 3.0 api |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 VERSION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
v0.71 |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use WWW::Bugzilla3; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $bz = new WWW::Bugzilla3(site => 'bugz.somesite.org'); |
30
|
|
|
|
|
|
|
$bz->login('user@host.org', 'PaSsWoRd'); |
31
|
|
|
|
|
|
|
... |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 FUNCTIONS |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=cut |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub _post($$) { |
38
|
|
|
|
|
|
|
my ($self, $u, $c) = @_; |
39
|
|
|
|
|
|
|
my $wrq = new HTTP::Request(POST => $u); |
40
|
|
|
|
|
|
|
$wrq->content($c); |
41
|
|
|
|
|
|
|
my $wrs = $self->{rpc}->useragent->request($wrq); |
42
|
|
|
|
|
|
|
return $wrs->content; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head2 new() |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Creates new Bugzilla3 object. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=cut |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub new($%) { |
52
|
|
|
|
|
|
|
my ($class, %param) = @_; |
53
|
|
|
|
|
|
|
croak "Cannot create $class without 'site'\n" unless $param{site}; |
54
|
|
|
|
|
|
|
$param{site} = 'http://' . $param{site} unless $param{site} =~ m|^https?://|i; |
55
|
|
|
|
|
|
|
$param{site} .= "/" unless $param{site} =~ /\/$/; |
56
|
|
|
|
|
|
|
$param{rpcurl} = $param{site} . 'xmlrpc.cgi'; |
57
|
|
|
|
|
|
|
$param{rpc} = RPC::XML::Client->new($param{rpcurl}); |
58
|
|
|
|
|
|
|
$param{rpc}->error_handler(sub { croak shift }); |
59
|
|
|
|
|
|
|
$param{rpc}->fault_handler(sub { croak shift->{faultString}->value }); |
60
|
|
|
|
|
|
|
$param{rpc}->useragent->cookie_jar({}); |
61
|
|
|
|
|
|
|
bless \%param, $class; |
62
|
|
|
|
|
|
|
return \%param; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 login |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
in: login, password |
68
|
|
|
|
|
|
|
out: user_id |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Logs into bugzilla. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub login($$$) { |
75
|
|
|
|
|
|
|
shift->{rpc}->simple_request('User.login', { |
76
|
|
|
|
|
|
|
'login' => shift, |
77
|
|
|
|
|
|
|
'password' => shift, |
78
|
|
|
|
|
|
|
})->{id}; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head2 logout |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Logs out. Does nothing if you are not logged in. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub logout($) { |
88
|
|
|
|
|
|
|
shift->{rpc}->simple_request('User.logout'); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 offer_account_by_email |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
in: email |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Sends an email to the user, offering to create an account. The user will have to click on a URL in the email, and choose their password and real name. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=cut |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub offer_account_by_email($$) { |
100
|
|
|
|
|
|
|
shift->{rpc}->simple_request('User.offer_account_by_email', { |
101
|
|
|
|
|
|
|
email => shift, |
102
|
|
|
|
|
|
|
}); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 create_user |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
in: email, full_name, password |
108
|
|
|
|
|
|
|
out: user_id |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Creates a user account directly in Bugzilla. Returns id of newly created user. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=cut |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub create_user($$$$) { |
115
|
|
|
|
|
|
|
shift->{rpc}->simple_request('User.create', { |
116
|
|
|
|
|
|
|
email => shift, |
117
|
|
|
|
|
|
|
full_name => shift, |
118
|
|
|
|
|
|
|
password => shift, |
119
|
|
|
|
|
|
|
})->{id}; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 get_selectable_products |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
out: ids |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
Returns an array of the ids of the products the user can search on. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=cut |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub get_selectable_products($) { |
131
|
|
|
|
|
|
|
@{shift->{rpc}->simple_request('Product.get_selectable_products')->{ids}}; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 get_enterable_products |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
out: ids |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Returns an array of the ids of the products the user can enter bugs against. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub get_enterable_products($) { |
143
|
|
|
|
|
|
|
@{shift->{rpc}->simple_request('Product.get_enterable_products')->{ids}}; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head2 get_accessible_products |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
out: ids |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Returns an array of the ids of the products the user can search or enter bugs against. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=cut |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub get_accessible_products($) { |
155
|
|
|
|
|
|
|
@{shift->{rpc}->simple_request('Product.get_accessible_products')->{ids}}; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head2 get_products |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
in: ids |
161
|
|
|
|
|
|
|
out: products |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Returns an array of hashes. Each hash describes a product, and has the following items: id, name, description, and internals. |
164
|
|
|
|
|
|
|
Internals is an internal representation of the product and can be changed by bugzilla at any time. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=cut |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub get_products($@) { |
169
|
|
|
|
|
|
|
my ($self, @ids) = @_; |
170
|
|
|
|
|
|
|
@{$self->{rpc}->simple_request('Product.get_products', { |
171
|
|
|
|
|
|
|
'ids' => \@ids, |
172
|
|
|
|
|
|
|
})->{products}}; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 version |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
out: version |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Returns bugzilla version. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub version($) { |
184
|
|
|
|
|
|
|
shift->{rpc}->simple_request('Bugzilla.version')->{version}; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 timezone |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
out: timezone |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Returns the timezone of the server Bugzilla is running on. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=cut |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub timezone($) { |
196
|
|
|
|
|
|
|
shift->{rpc}->simple_request('Bugzilla.timezone')->{timezone}; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=head2 legal_values |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
in: field, product_id |
202
|
|
|
|
|
|
|
out: values |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Returns an array of values that are allowed for a particular field. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub legal_values($$$) { |
209
|
|
|
|
|
|
|
@{shift->{rpc}->simple_request('Bug.legal_values', { |
210
|
|
|
|
|
|
|
field => shift, |
211
|
|
|
|
|
|
|
product_id => shift, |
212
|
|
|
|
|
|
|
})->{'values'}}; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 get_bugs |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
in: ids |
218
|
|
|
|
|
|
|
out: bugs |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Gets information about particular bugs in the database. ids is an array of numbers and strings. Returns an array of hashes. Each hash contains the following items: |
221
|
|
|
|
|
|
|
id - The numeric bug_id of this bug. |
222
|
|
|
|
|
|
|
alias - The alias of this bug. If there is no alias or aliases are disabled in this Bugzilla, this will be an empty string. |
223
|
|
|
|
|
|
|
summary - The summary of this bug. |
224
|
|
|
|
|
|
|
creation_time - When the bug was created. |
225
|
|
|
|
|
|
|
last_change_time - When the bug was last changed. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=cut |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub get_bugs($@) { |
230
|
|
|
|
|
|
|
my ($self, @ids) = @_; |
231
|
|
|
|
|
|
|
@{$self->{rpc}->simple_request('Bug.get_bugs', { |
232
|
|
|
|
|
|
|
'ids' => \@ids, |
233
|
|
|
|
|
|
|
})->{bugs}}; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 create_bug |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
This allows you to create a new bug in Bugzilla. If you specify any invalid fields, they will be ignored. If you specify any fields you are not allowed to set, they will just be set to their defaults or ignored. |
239
|
|
|
|
|
|
|
Some params must be set, or an error will be thrown. These params are marked Required. |
240
|
|
|
|
|
|
|
Some parameters can have defaults set in Bugzilla, by the administrator. If these parameters have defaults set, you can omit them. These parameters are marked Defaulted. |
241
|
|
|
|
|
|
|
Clients that want to be able to interact uniformly with multiple Bugzillas should always set both the params marked Required and those marked Defaulted, because some Bugzillas may not have defaults set for Defaulted parameters, and then this method will throw an error if you don't specify them. |
242
|
|
|
|
|
|
|
The descriptions of the parameters below are what they mean when Bugzilla is being used to track software bugs. They may have other meanings in some installations. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
product (string) Required - The name of the product the bug is being filed against. |
245
|
|
|
|
|
|
|
component (string) Required - The name of a component in the product above. |
246
|
|
|
|
|
|
|
summary (string) Required - A brief description of the bug being filed. |
247
|
|
|
|
|
|
|
version (string) Required - A version of the product above; the version the bug was found in. |
248
|
|
|
|
|
|
|
description (string) Defaulted - The initial description for this bug. Some Bugzilla installations require this to not be blank. |
249
|
|
|
|
|
|
|
op_sys (string) Defaulted - The operating system the bug was discovered on. |
250
|
|
|
|
|
|
|
platform (string) Defaulted - What type of hardware the bug was experienced on. |
251
|
|
|
|
|
|
|
priority (string) Defaulted - What order the bug will be fixed in by the developer, compared to the developer's other bugs. |
252
|
|
|
|
|
|
|
severity (string) Defaulted - How severe the bug is. |
253
|
|
|
|
|
|
|
alias (string) - A brief alias for the bug that can be used instead of a bug number when accessing this bug. Must be unique in all of this Bugzilla. |
254
|
|
|
|
|
|
|
assigned_to (username) - A user to assign this bug to, if you don't want it to be assigned to the component owner. |
255
|
|
|
|
|
|
|
cc (array) - An array of usernames to CC on this bug. |
256
|
|
|
|
|
|
|
qa_contact (username) - If this installation has QA Contacts enabled, you can set the QA Contact here if you don't want to use the component's default QA Contact. |
257
|
|
|
|
|
|
|
status (string) - The status that this bug should start out as. Note that only certain statuses can be set on bug creation. |
258
|
|
|
|
|
|
|
target_milestone (string) - A valid target milestone for this product. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
In addition to the above parameters, if your installation has any custom fields, you can set them just by passing in the name of the field and its value as a string. |
261
|
|
|
|
|
|
|
Returns one element, id. This is the id of the newly-filed bug. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=cut |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub create_bug($%) { |
266
|
|
|
|
|
|
|
my ($self, %p) = @_; |
267
|
|
|
|
|
|
|
my $rs = $self->{rpc}->simple_request('Bug.create', \%p)->{id}; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=head2 named_search |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
in: name |
273
|
|
|
|
|
|
|
out: ids |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Execute saved search. Returns list of bugs. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=cut |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub named_search($$) { |
280
|
|
|
|
|
|
|
shift->search(cmdtype => 'runnamed', namedcmd => shift); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=head2 search |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Execute search. Returns list of bugs. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=cut |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub search($%) { |
290
|
|
|
|
|
|
|
my ($self, %param) = @_; |
291
|
|
|
|
|
|
|
$param{ctype} = 'atom'; |
292
|
|
|
|
|
|
|
my $r = $self->_post($self->{site} . 'buglist.cgi', join '&', |
293
|
|
|
|
|
|
|
map { uri_escape($_) . "=" . uri_escape($param{$_}) } keys %param); |
294
|
|
|
|
|
|
|
return grep s/^.*.*?\?id=(\d+)<\/id>.*$/$1/, split "\n", $r; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head2 ua |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
out: useragent |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Returns LWP::UserAgent object user for communications with bugzilla. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=cut |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub ua($) { |
306
|
|
|
|
|
|
|
shift->{rpc}->useragent; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
1; |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=head1 AUTHOR |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Alexey Alexandrov, C<< >> |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=head1 BUGS |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
318
|
|
|
|
|
|
|
C, or through the web interface at |
319
|
|
|
|
|
|
|
L. |
320
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
321
|
|
|
|
|
|
|
your bug as I make changes. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head1 SUPPORT |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
perldoc WWW::Bugzilla3 |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
You can also look for information at: |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=over 4 |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
L |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item * CPAN Ratings |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
L |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
L |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=item * Search CPAN |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
L |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=back |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Copyright 2007 Alexey Alexandrov, all rights reserved. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
356
|
|
|
|
|
|
|
under the same terms as Perl itself. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=cut |
359
|
|
|
|
|
|
|
|