line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################# |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# An interface to Fedora's Bugzilla instance. |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Author: Chris Weyl (cpan:RSRCHBOY), <cweyl@alumni.drew.edu> |
6
|
|
|
|
|
|
|
# Company: No company, personal work |
7
|
|
|
|
|
|
|
# Created: 12/29/2008 11:06:54 AM PST |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# Copyright (c) 2008 Chris Weyl <cweyl@alumni.drew.edu> |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# This library is free software; you can redistribute it and/or |
12
|
|
|
|
|
|
|
# modify it under the terms of the GNU Lesser General Public |
13
|
|
|
|
|
|
|
# License as published by the Free Software Foundation; either |
14
|
|
|
|
|
|
|
# version 2.1 of the License, or (at your option) any later version. |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
############################################################################# |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package Fedora::Bugzilla; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# moose core |
21
|
3
|
|
|
3
|
|
98529
|
use Moose; |
|
3
|
|
|
|
|
2499855
|
|
|
3
|
|
|
|
|
30
|
|
22
|
3
|
|
|
3
|
|
27733
|
use Moose::Util::TypeConstraints; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
31
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# moose extensions |
25
|
3
|
|
|
3
|
|
21918
|
use MooseX::Types::Path::Class qw{ File Dir }; |
|
3
|
|
|
|
|
651210
|
|
|
3
|
|
|
|
|
48
|
|
26
|
3
|
|
|
3
|
|
20724
|
use MooseX::Types::URI qw{ Uri }; |
|
3
|
|
|
|
|
292986
|
|
|
3
|
|
|
|
|
26
|
|
27
|
3
|
|
|
3
|
|
12421
|
use MooseX::AttributeHelpers; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# other fedora bits |
30
|
|
|
|
|
|
|
use Fedora::Bugzilla::Bug; |
31
|
|
|
|
|
|
|
use Fedora::Bugzilla::NewBug; |
32
|
|
|
|
|
|
|
use Fedora::Bugzilla::Bugs; |
33
|
|
|
|
|
|
|
use Fedora::Bugzilla::QueriedBugs; |
34
|
|
|
|
|
|
|
use Fedora::Bugzilla::Types ':all'; |
35
|
|
|
|
|
|
|
use Fedora::Bugzilla::XMLRPC; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# cpan bits |
38
|
|
|
|
|
|
|
use Path::Class qw{ file dir }; |
39
|
|
|
|
|
|
|
use Regexp::Common; |
40
|
|
|
|
|
|
|
use HTTP::Cookies; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# debugging |
43
|
|
|
|
|
|
|
#use Smart::Comments '###'; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
use namespace::clean -except => 'meta'; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
our $VERSION = '0.13'; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
## not needed ATM |
50
|
|
|
|
|
|
|
#subtype 'HTTP::Cookies' |
51
|
|
|
|
|
|
|
# => as Object |
52
|
|
|
|
|
|
|
# => where { $_->isa('HTTP::Cookies') } |
53
|
|
|
|
|
|
|
# ; |
54
|
|
|
|
|
|
|
# |
55
|
|
|
|
|
|
|
#coerce 'HTTP::Cookies' |
56
|
|
|
|
|
|
|
# => from 'Path::Class::File' |
57
|
|
|
|
|
|
|
# => via { HTTP::Cookies->new(file => "$_") } |
58
|
|
|
|
|
|
|
# ; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# we could require one or the other be set, but there are many operations we |
61
|
|
|
|
|
|
|
# can do against bugzilla that don't actually require we be logged in |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
has site => (is => 'ro', lazy => 1, isa => Uri, coerce => 1, lazy_build => 1); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
has userid => (is => 'ro', isa => 'Str', lazy_build => 1); |
66
|
|
|
|
|
|
|
has userid_cb => (is => 'ro', isa => 'CodeRef', lazy_build => 1); |
67
|
|
|
|
|
|
|
has passwd => (is => 'ro', isa => 'Str', lazy_build => 1); |
68
|
|
|
|
|
|
|
has passwd_cb => (is => 'ro', isa => 'CodeRef', lazy_build => 1); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _build_site { 'https://bugzilla.redhat.com/xmlrpc.cgi' } |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub _build_userid { shift->userid_cb->() } |
73
|
|
|
|
|
|
|
sub _build_userid_cb { sub { die 'neither userid nor userid_cb set' } } |
74
|
|
|
|
|
|
|
sub _build_passwd { shift->passwd_cb->() } |
75
|
|
|
|
|
|
|
sub _build_passwd_cb { sub { die 'neither passwd nor userid_cb set' } } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
has new_bug_class => (is => 'rw', isa => 'Str', lazy_build => 1); |
78
|
|
|
|
|
|
|
has default_bug_class => (is => 'rw', isa => 'Str', lazy_build => 1); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub _build_new_bug_class { 'Fedora::Bugzilla::NewBug' } |
81
|
|
|
|
|
|
|
sub _build_default_bug_class { 'Fedora::Bugzilla::Bug' } |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
has aggressive_fetch => (is => 'rw', isa => 'Bool', lazy_build => 1); |
84
|
|
|
|
|
|
|
sub _build_aggressive_fetch { 1 } |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# hold our RPC::XML::Client instance |
87
|
|
|
|
|
|
|
has rpc => (is => 'ro', isa => 'Fedora::Bugzilla::XMLRPC', lazy_build => 1); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# create our RPC::XML::Client appropriately |
90
|
|
|
|
|
|
|
sub _build_rpc { |
91
|
|
|
|
|
|
|
my $self = shift @_; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# twice to keep warnings from complaining... |
94
|
|
|
|
|
|
|
#local $RPC::XML::ENCODING; |
95
|
|
|
|
|
|
|
$RPC::XML::ENCODING = 'UTF-8'; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
my $rpc = Fedora::Bugzilla::XMLRPC->new($self->site, sub { $self->login }); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# error bits |
100
|
|
|
|
|
|
|
$rpc->error_handler($self->rpc_error_handler); |
101
|
|
|
|
|
|
|
$rpc->fault_handler($self->rpc_fault_handler); |
102
|
|
|
|
|
|
|
$rpc->useragent->cookie_jar($self->cookie_jar); |
103
|
|
|
|
|
|
|
$rpc->useragent->agent($self->ua_agent); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
return $rpc; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
has ua_agent => (is => 'ro', isa => 'Str', lazy_build => 1); |
109
|
|
|
|
|
|
|
has ua => (is => 'ro', isa => 'LWP::UserAgent', lazy_build => 1); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _build_ua_agent { "Fedora::Bugzilla $VERSION" } |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub _build_ua { |
114
|
|
|
|
|
|
|
my $self = shift @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
return LWP::UserAgent->new( |
117
|
|
|
|
|
|
|
cookie_jar => $self->cookie_jar, |
118
|
|
|
|
|
|
|
agent => $self->ua_agent, |
119
|
|
|
|
|
|
|
); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
has rpc_error_handler => (is => 'ro', isa => 'CodeRef', lazy_build => 1); |
123
|
|
|
|
|
|
|
has rpc_fault_handler => (is => 'ro', isa => 'CodeRef', lazy_build => 1); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub _build_rpc_error_handler { sub { confess shift } } |
126
|
|
|
|
|
|
|
sub _build_rpc_fault_handler { sub { confess shift->{faultString}->value } } |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
has cookie_file => (is => 'ro', isa => File, coerce => 1, lazy_build => 1); |
129
|
|
|
|
|
|
|
has cookie_jar => (is => 'ro', isa => 'HTTP::Cookies', lazy_build => 1); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub _build_cookie_file { file "$ENV{HOME}/.fedora.bz.cookies.txt" } |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _build_cookie_jar { |
134
|
|
|
|
|
|
|
my $self = shift @_; |
135
|
|
|
|
|
|
|
my $file = $self->cookie_file; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# if set to undef, we don't want the cookies to be saved anywhere |
138
|
|
|
|
|
|
|
return HTTP::Cookies->new if not defined $file; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# if file exists and is writeable, or dir exists and is writeable, use it |
141
|
|
|
|
|
|
|
return HTTP::Cookies->new(file => $file, autosave => 1) |
142
|
|
|
|
|
|
|
if (-f $file && -w _) || (-d $file->dir && -w _); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# otherwise, we have a file defined but we can't write to it / dir |
145
|
|
|
|
|
|
|
warn "cookie_file ($file) is not usable (write errors)"; |
146
|
|
|
|
|
|
|
return HTTP::Cookies->new; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# this seems a little magical, but really, makes sense to me :) |
150
|
|
|
|
|
|
|
has login => ( |
151
|
|
|
|
|
|
|
is => 'ro', |
152
|
|
|
|
|
|
|
lazy => 1, |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
predicate => 'logged_in', |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
clearer => 'logout', |
157
|
|
|
|
|
|
|
trigger => sub { shift->_logout }, |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
default => sub { |
160
|
|
|
|
|
|
|
my $self = shift @_; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
### logging in... |
163
|
|
|
|
|
|
|
my $ret = $self->rpc->simple_request( |
164
|
|
|
|
|
|
|
'User.login', |
165
|
|
|
|
|
|
|
{ |
166
|
|
|
|
|
|
|
login => $self->userid, |
167
|
|
|
|
|
|
|
password => $self->passwd, |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
#)->{id}; |
170
|
|
|
|
|
|
|
); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
### $ret |
173
|
|
|
|
|
|
|
#die; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
return $ret->{id} if $ret; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
die 'Could not log in to bugzilla! (password problem?)'; |
178
|
|
|
|
|
|
|
}, |
179
|
|
|
|
|
|
|
); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _logout { shift->rpc->simple_request('User.logout') } |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Product.get_accessible_products |
184
|
|
|
|
|
|
|
has accessible_products => ( |
185
|
|
|
|
|
|
|
is => 'ro', |
186
|
|
|
|
|
|
|
isa => 'ArrayRef[Int]', |
187
|
|
|
|
|
|
|
auto_deref => 1, |
188
|
|
|
|
|
|
|
lazy_build => 1, |
189
|
|
|
|
|
|
|
); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
######################################################################## |
192
|
|
|
|
|
|
|
# misc bugzilla functionality |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Bugzilla.version |
195
|
|
|
|
|
|
|
has version => (is => 'ro', isa => 'Str', lazy_build => 1); |
196
|
|
|
|
|
|
|
sub _build_version { shift->rpc->simple_request('Bugzilla.version')->{version} } |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Bugzilla.timezone |
199
|
|
|
|
|
|
|
has timezone => (is => 'ro', isa => 'Str', lazy_build => 1); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub _build_timezone { |
202
|
|
|
|
|
|
|
shift->rpc->simple_request('Bugzilla.timezone')->{timezone} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# User.offer_account_by_email |
206
|
|
|
|
|
|
|
sub offer_account_by_email { |
207
|
|
|
|
|
|
|
my $self = shift @_; |
208
|
|
|
|
|
|
|
my $email = shift @_ || confess 'Must pass email address'; |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $r = $self->rpc->simple_request( |
211
|
|
|
|
|
|
|
'User.offer_account_by_email', |
212
|
|
|
|
|
|
|
{ email => $email}, |
213
|
|
|
|
|
|
|
); |
214
|
|
|
|
|
|
|
### $r |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
return; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# User.create |
220
|
|
|
|
|
|
|
sub create_user { |
221
|
|
|
|
|
|
|
my $self = shift @_; |
222
|
|
|
|
|
|
|
my %info = @_; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# FIXME need checking for parameters here... |
225
|
|
|
|
|
|
|
return $self->rpc->simple_request('User.create', \%info)->{id}; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
######################################################################## |
229
|
|
|
|
|
|
|
# products |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
#has _products => ( ... ); |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# a little sugar to get us to the same name as WWW::Bugzilla3/Bugzilla |
234
|
|
|
|
|
|
|
# internals |
235
|
|
|
|
|
|
|
sub get_accessible_products { shift->accesible_products } |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub _build_get_accessible_products { |
238
|
|
|
|
|
|
|
my $self = shift @_; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
$self->rpc->simple_request('Product.get_accessible_products')->{ids}; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
######################################################################## |
244
|
|
|
|
|
|
|
# fetch/create/etc bugs |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub create_bug { |
247
|
|
|
|
|
|
|
my $self = shift @_; |
248
|
|
|
|
|
|
|
my $nb; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
if ( ! (blessed $_[0] && $_[0]->isa('Fedora::Bugzilla::NewBug')) ) { |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# we wern't passed a new bug object, so let's create one. |
253
|
|
|
|
|
|
|
$nb = $self->new_bug_class->new(@_); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
else { |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
$nb = shift @_; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# actually create the bug on the server |
261
|
|
|
|
|
|
|
my $id = $self->_create_bug($nb->bughash); |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
return Fedora::Bugzilla::Bug->new(bz => $self, id => $id); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# Bug.create |
268
|
|
|
|
|
|
|
sub _create_bug { |
269
|
|
|
|
|
|
|
my $self = shift @_; |
270
|
|
|
|
|
|
|
my $bughash = shift @_; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# FIXME this needs work |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
#my $req = RPC::XML::request->new('Bug.create', $bughash); |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# no validation! |
277
|
|
|
|
|
|
|
return $self->rpc->simple_request('Bug.create', $bughash)->{id}; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub get_bug { shift->bug(@_) } |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub bug { |
283
|
|
|
|
|
|
|
my $self = shift @_; |
284
|
|
|
|
|
|
|
my $bug = shift @_ || confess 'Must pass bug id or alias'; |
285
|
|
|
|
|
|
|
my $class = shift @_ || $self->default_bug_class; |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# invoke accordingly |
288
|
|
|
|
|
|
|
return $class->new(bz => $self, id => $bug) if $bug =~ $RE{num}{int}; |
289
|
|
|
|
|
|
|
return $class->new(bz => $self, alias => $bug); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub get_bugs { shift->bugs(@_) } |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# Bug.get_bugs |
295
|
|
|
|
|
|
|
sub bugs { Fedora::Bugzilla::Bugs->new(bz => shift, ids => [ @_ ]) }; |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
sub get_bug_fields { shift->all_legal_bug_fields } |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# bugzilla.getBugFields |
300
|
|
|
|
|
|
|
has all_legal_bug_fields => ( |
301
|
|
|
|
|
|
|
metaclass => 'Collection::List', |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
is => 'ro', |
304
|
|
|
|
|
|
|
isa => 'ArrayRef[Str]', |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
auto_deref => 1, |
307
|
|
|
|
|
|
|
lazy_build => 1, |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# provides ... |
310
|
|
|
|
|
|
|
); |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub _build_all_legal_bug_fields { |
313
|
|
|
|
|
|
|
my $self = shift @_; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my $fields = $self |
316
|
|
|
|
|
|
|
->rpc |
317
|
|
|
|
|
|
|
->simple_request('bugzilla.getBugFields') |
318
|
|
|
|
|
|
|
; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
return [ sort @$fields ]; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
######################################################################## |
324
|
|
|
|
|
|
|
# Searching... |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# this is still pretty experimental, and seems to be specific to RHBZ at the |
327
|
|
|
|
|
|
|
# moment. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
has queryinfo => ( |
330
|
|
|
|
|
|
|
is => 'ro', |
331
|
|
|
|
|
|
|
isa => 'HashRef', |
332
|
|
|
|
|
|
|
lazy_build => 1, |
333
|
|
|
|
|
|
|
); |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub _build_queryinfo { |
336
|
|
|
|
|
|
|
my $self = shift @_; |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
warn q{This probably won't work; if it does, please email the author}; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
my $foo = $self->rpc->simple_request('bugzilla.getQueryInfo'); |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
return $foo; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=begin comment |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
https://fedorahosted.org/python-bugzilla/browser/bugzilla/rhbugzilla.py |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
def _query(self,query): |
350
|
|
|
|
|
|
|
'''Query bugzilla and return a list of matching bugs. |
351
|
|
|
|
|
|
|
query must be a dict with fields like those in in querydata['fields']. |
352
|
|
|
|
|
|
|
You can also pass in keys called 'quicksearch' or 'savedsearch' - |
353
|
|
|
|
|
|
|
'quicksearch' will do a quick keyword search like the simple search |
354
|
|
|
|
|
|
|
on the Bugzilla home page. |
355
|
|
|
|
|
|
|
'savedsearch' should be the name of a previously-saved search to |
356
|
|
|
|
|
|
|
execute. You need to be logged in for this to work. |
357
|
|
|
|
|
|
|
Returns a dict like this: {'bugs':buglist, |
358
|
|
|
|
|
|
|
'sql':querystring} |
359
|
|
|
|
|
|
|
buglist is a list of dicts describing bugs, and 'sql' contains the SQL |
360
|
|
|
|
|
|
|
generated by executing the search. |
361
|
|
|
|
|
|
|
''' |
362
|
|
|
|
|
|
|
return self._proxy.Bug.search(query) |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=end comment |
365
|
|
|
|
|
|
|
=cut |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# Bug.search |
368
|
|
|
|
|
|
|
sub search { shift->_query(@_) } |
369
|
|
|
|
|
|
|
sub query { shift->_query(@_) } |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub run_named_query { shift->_query(savedsearch => shift) } |
372
|
|
|
|
|
|
|
sub run_savedsearch { shift->run_named_query(@_) } |
373
|
|
|
|
|
|
|
sub run_quicksearch { shift->_query(quicksearch => join(' ', @_)) } |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub _query { |
376
|
|
|
|
|
|
|
my $self = shift @_; |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
my $ret = $self->rpc->simple_request('Bug.search', { @_ }); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# FIXME nuke? |
381
|
|
|
|
|
|
|
$self->last_sql($ret->{sql}); |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
return Fedora::Bugzilla::QueriedBugs->new( |
384
|
|
|
|
|
|
|
bz => $self, |
385
|
|
|
|
|
|
|
raw => $ret->{bugs}, |
386
|
|
|
|
|
|
|
sql => $ret->{sql}, |
387
|
|
|
|
|
|
|
display_columns => $ret->{displaycolumns}, |
388
|
|
|
|
|
|
|
); |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
has last_sql => ( |
392
|
|
|
|
|
|
|
is => 'rw', |
393
|
|
|
|
|
|
|
isa => 'Str', |
394
|
|
|
|
|
|
|
predicate => 'has_last_sql', |
395
|
|
|
|
|
|
|
clearer => 'clear_last_sql', |
396
|
|
|
|
|
|
|
); |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
######################################################################## |
399
|
|
|
|
|
|
|
# magic end bits |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
1; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
__END__ |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=head1 NAME |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
Fedora::Bugzilla - Interact with Fedora's bugzilla instance |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=head1 SYNOPSIS |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
use Fedora::Bugzilla; |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=for author to fill in: |
414
|
|
|
|
|
|
|
Brief code example(s) here showing commonest usage(s). |
415
|
|
|
|
|
|
|
This section will be as far as many users bother reading |
416
|
|
|
|
|
|
|
so make it as educational and exeplary as possible. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head1 DESCRIPTION |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
The XML-RPC interface to bugzilla is a quite useful, and while bugzilla 3.x |
421
|
|
|
|
|
|
|
is starting to flesh their interface out a bit more (see, e.g., |
422
|
|
|
|
|
|
|
L<WWW::Bugzilla3>), Fedora's bugzilla implementation has a large number of |
423
|
|
|
|
|
|
|
custom methods. This module aims to expose them, in a kinder, gentler way. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
In addition to the XML-RPC methods Bugzilla makes available, there are also |
426
|
|
|
|
|
|
|
some things we only seem to be able to access via the web/XML interfaces. |
427
|
|
|
|
|
|
|
(See, e.g., the flags, attachments and comments functionality.) This package |
428
|
|
|
|
|
|
|
works to expose those as well. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Some functionality is more expensive to invoke than others, for a variety of |
431
|
|
|
|
|
|
|
reasons. We strive to only access each bit as we need it, to minimize time |
432
|
|
|
|
|
|
|
and effort while still making available as much as is possible. |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
(And, yes, I know it's really RedHat's bugzilla. Some day... oh yes, some |
435
|
|
|
|
|
|
|
day...) |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=head1 INTERFACE |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
"Release Early, Release Often" |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
I've tried to get at least the methods I use in here. I know I'm missing |
442
|
|
|
|
|
|
|
some, and I bet there are others I don't even know about... I'll try not to, |
443
|
|
|
|
|
|
|
but I won't guarantee that I won't change the api in some incompatable way. |
444
|
|
|
|
|
|
|
If you'd like to see something here, please either drop me a line (see AUTHOR) |
445
|
|
|
|
|
|
|
or better yet, open a ticket with a patch ;) |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Note also, the documentation is woefully incomplete. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head2 METHODS |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=over |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item B<new> |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Standard constructor. Takes a number of arguments, two of which are |
456
|
|
|
|
|
|
|
required; note that each of these arguments is also available through an |
457
|
|
|
|
|
|
|
accessor of the same name once the object instance has been created. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=over |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=item I<userid =E<gt> Str> |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
B<Required.> Your bugzilla userid (generally your email address). |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=item I<passwd =E<gt> Str> |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
B<Required.> Your bugzilla password. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=item I<site =E<gt> Str|URI> |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
The URI of the interface you're trying to access. Note this (correctly) |
472
|
|
|
|
|
|
|
defaults to L<https://bugzilla.redhat.com/xmlrpc.cgi>. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=item I<cookie_file =E<gt> Str|Path::Class::File> |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
Takes a filename to give to the RPC's useragent instance as file to hold the |
477
|
|
|
|
|
|
|
bugzilla cookies in. Set to undef to use no actual file, and just cache |
478
|
|
|
|
|
|
|
cookies in-memory. |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Defaults to: "$ENV{HOME}/.fedora.bz.cookies.txt"; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=back |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=item B<login> |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Log in to the bugzilla service. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=item B<logged_in> |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
True if we're logged in, false otherwise. |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=item B<logout> |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
Log out from the bugzilla service. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=back |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 BUG CREATION |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=over |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item B<create_bug> |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Creates a new bug, passing @_ to the constructor of the default new bug class. |
505
|
|
|
|
|
|
|
See L<Fedora::Bugzilla::NewBug>. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=item B<new_bug_class> |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Gets/sets the class used to create new bugs with. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=back |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=head2 FETCHING BUGS |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=over |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=item B<bug, get_bug (Int|Str)> |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
Given a bug id/alias, returns a corresponding L<Fedora::Bugzilla::Bug>. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=item B<bugs, get_bugs (Int|Str, ...)> |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Given a list of bug id/aliases, return a L<Fedora::Bugzilla::Bugs> object. |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=back |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 SEARCHING AND QUERYING |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
These functions return a L<Fedora::Bugzilla::Bugs> object representing the |
530
|
|
|
|
|
|
|
results of the query. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=over |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=item B<run_savedsearch(Str)> |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Given the name of a saved search, run it and return the bugs. |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=item B<run_named_query(Str)> |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
Alias to run_savedsearch(). |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=item B<run_quicksearch(Str, ...)> |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Given a number of search terms, submit to Bugzilla for a quicksearch (akin to |
545
|
|
|
|
|
|
|
entering terms on the web UI). |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=back |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=head2 MISC SERVER METHODS |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=over |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item B<accessible_products> |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
FIXME. Returns an array of products the user can search or enter bugs against. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=item B<version> |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
Returns the version of the bugzilla server. |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=item B<timezone> |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
Returns the timezone the bugzilla server is in. |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=item B<offer_account_by_email (email address)> |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
Sends an offer of a Bugzilla account to the given email address. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=back |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head1 SPEED |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
We've tried to take steps to make sure things are speedy: "non-changing" |
574
|
|
|
|
|
|
|
values are cached and only pulled when needed, etc. While we don't |
575
|
|
|
|
|
|
|
implement a multicall queued approach (yet), we do try to minimize the number |
576
|
|
|
|
|
|
|
of queries required; e.g. by using Bug.get_bugs when multiple bugs are needed. |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
Some of the functionality requires that the XML representation of the bug be |
579
|
|
|
|
|
|
|
pulled (e.g. flags, comments, attachment listings, etc); in these cases we |
580
|
|
|
|
|
|
|
don't do the actual pull until requested. |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
For methods that return more than one bug wrapped in a |
583
|
|
|
|
|
|
|
L<Fedora::Bugzilla::Bugs> object, we fetch all the bug data through one XMLRPC |
584
|
|
|
|
|
|
|
call once someone tries to access any of the bug data in it (e.g. bugs(), |
585
|
|
|
|
|
|
|
num_bugs(), etc). Additionally, if I<aggressive_fetch> is set in the parent |
586
|
|
|
|
|
|
|
Fedora::Bugzilla object, we'll pull down the XML and any other data we need |
587
|
|
|
|
|
|
|
for each bug. Pulling all the data at one time can result in significant time |
588
|
|
|
|
|
|
|
savings over having each bug object pull their own. |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 Updates |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
Note that performing an action on a bug that changes any value will result in |
593
|
|
|
|
|
|
|
all data (save the id) being discarded, and reloaded the next time the bug is |
594
|
|
|
|
|
|
|
accessed. It's best to pull any information you may need _before_ updating |
595
|
|
|
|
|
|
|
the bug, if the situation warrants it, to avoid the second call to the |
596
|
|
|
|
|
|
|
Bugzilla server. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
At the moment, we generally die() or confess() any errors. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head1 BUGS, LIMITATIONS AND VERSION CONTROL |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Source, tickets, etc can be all accessed through the Camelus project at |
605
|
|
|
|
|
|
|
fedorahosted.org. Please use the 'Fedora-Bugzilla' component when reporting |
606
|
|
|
|
|
|
|
issues or making feature requests: |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
L<http://camelus.fedorahosted.org> |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
There are still many areas of functionality we do not handle yet. If you'd |
611
|
|
|
|
|
|
|
like to see something in here, specific or otherwise, please make a feature |
612
|
|
|
|
|
|
|
request through the trac ticketing interface. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head1 SEE ALSO |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
L<http://www.bugzilla.org>, L<http://bugzilla.redhat.com>, |
617
|
|
|
|
|
|
|
L<http://python-bugzilla.fedorahosted.org>, the L<WWW::Bugzilla3> module. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head1 AUTHOR |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
Chris Weyl C<< <cweyl@alumni.drew.edu> >> |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
Copyright (c) 2008, Chris Weyl <cweyl@alumni.drew.edu> |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify it under |
629
|
|
|
|
|
|
|
the terms of the GNU Lesser General Public License as published by the Free |
630
|
|
|
|
|
|
|
Software Foundation; either version 2.1 of the License, or (at your option) |
631
|
|
|
|
|
|
|
any later version. |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful, but WITHOUT |
634
|
|
|
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
635
|
|
|
|
|
|
|
OR A PARTICULAR PURPOSE. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
See the GNU Lesser General Public License for more details. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License |
640
|
|
|
|
|
|
|
along with this library; if not, write to the |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Free Software Foundation, Inc., |
643
|
|
|
|
|
|
|
59 Temple Place, Suite 330, |
644
|
|
|
|
|
|
|
Boston, MA 02111-1307 USA |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=cut |