425 lines
11 KiB
Perl
Executable File
425 lines
11 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
#
|
|
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
|
# defined by the Mozilla Public License, v. 2.0.
|
|
|
|
=head1 NAME
|
|
|
|
bz_webservice_demo.pl - Show how to talk to Bugzilla via XMLRPC
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
C<bz_webservice_demo.pl [options]>
|
|
|
|
C<bz_webservice_demo.pl --help> for detailed help
|
|
|
|
=cut
|
|
|
|
use 5.10.1;
|
|
use strict;
|
|
use warnings;
|
|
|
|
use lib qw(lib);
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
use File::Basename qw(dirname);
|
|
use File::Spec;
|
|
use XMLRPC::Lite;
|
|
|
|
# If you want, say “use Bugzilla::WebService::Constants” here to get access
|
|
# to Bugzilla's web service error code constants.
|
|
# If you do this, remember to issue a “use lib” pointing to your Bugzilla
|
|
# installation directory, too.
|
|
|
|
my $help;
|
|
my $Bugzilla_uri;
|
|
my $Bugzilla_login;
|
|
my $Bugzilla_password;
|
|
my $Bugzilla_restrict;
|
|
my $Bugzilla_token;
|
|
my $bug_id;
|
|
my $product_name;
|
|
my $create_file_name;
|
|
my $legal_field_values;
|
|
my $add_comment;
|
|
my $private;
|
|
my $work_time;
|
|
my $fetch_extension_info = 0;
|
|
my $debug;
|
|
|
|
GetOptions('help|h|?' => \$help,
|
|
'uri=s' => \$Bugzilla_uri,
|
|
'login:s' => \$Bugzilla_login,
|
|
'password=s' => \$Bugzilla_password,
|
|
'restrictlogin!' => \$Bugzilla_restrict,
|
|
'bug_id:s' => \$bug_id,
|
|
'product_name:s' => \$product_name,
|
|
'create:s' => \$create_file_name,
|
|
'field:s' => \$legal_field_values,
|
|
'comment:s' => \$add_comment,
|
|
'private:i' => \$private,
|
|
'worktime:f' => \$work_time,
|
|
'extension_info' => \$fetch_extension_info,
|
|
'debug' => \$debug
|
|
) or pod2usage({'-verbose' => 0, '-exitval' => 1});
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over
|
|
|
|
=item --help, -h, -?
|
|
|
|
Print a short help message and exit.
|
|
|
|
=item --uri
|
|
|
|
URI to Bugzilla's C<xmlrpc.cgi> script, along the lines of
|
|
C<http://your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>.
|
|
|
|
=item --login
|
|
|
|
Bugzilla login name. Specify this together with B<--password> in order to log in.
|
|
|
|
Specify this without a value in order to log out.
|
|
|
|
=item --password
|
|
|
|
Bugzilla password. Specify this together with B<--login> in order to log in.
|
|
|
|
=item --restrictlogin
|
|
|
|
Gives access to Bugzilla's "Bugzilla_restrictlogin" option.
|
|
Specify this option while logging in to restrict the login token to be
|
|
only valid from the IP address which called
|
|
Don't specify this option to do the same thing as unchecking the box.
|
|
|
|
See Bugzilla's restrictlogin parameter for details.
|
|
|
|
=item --bug_id
|
|
|
|
Pass a bug ID to have C<bz_webservice_demo.pl> do some bug-related test calls.
|
|
|
|
=item --product_name
|
|
|
|
Pass a product name to have C<bz_webservice_demo.pl> do some product-related
|
|
test calls.
|
|
|
|
=item --create
|
|
|
|
Specify a file that contains settings for the creating of a new bug.
|
|
|
|
=item --field
|
|
|
|
Pass a field name to get legal values for this field. It must be either a
|
|
global select field (such as bug_status, resolution, rep_platform, op_sys,
|
|
priority, bug_severity) or a custom select field.
|
|
|
|
=item --comment
|
|
|
|
A comment to add to a bug identified by B<--bug_id>. You must also pass a B<--login>
|
|
and B<--password> to log in to Bugzilla.
|
|
|
|
=item --private
|
|
|
|
An optional non-zero value to specify B<--comment> as private.
|
|
|
|
=item --worktime
|
|
|
|
An optional double precision number specifying the work time for B<--comment>.
|
|
|
|
=item --extension_info
|
|
|
|
If specified on the command line, the script returns the information about the
|
|
extensions that are installed.
|
|
|
|
=item --debug
|
|
|
|
Enable tracing at the debug level of XMLRPC requests and responses.
|
|
|
|
=back
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
=cut
|
|
|
|
pod2usage({'-verbose' => 1, '-exitval' => 0}) if $help;
|
|
_syntaxhelp('URI unspecified') unless $Bugzilla_uri;
|
|
|
|
# We will use this variable for SOAP call results.
|
|
my $soapresult;
|
|
|
|
# We will use this variable for function call results.
|
|
my $result;
|
|
|
|
=head2 Initialization
|
|
|
|
Using the XMLRPC::Lite class, you set up a proxy, as shown in this script.
|
|
Bugzilla's XMLRPC URI ends in C<xmlrpc.cgi>, so your URI looks along the lines
|
|
of C<http://your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>.
|
|
|
|
=cut
|
|
|
|
my $proxy = XMLRPC::Lite->proxy($Bugzilla_uri);
|
|
|
|
=head2 Debugging
|
|
|
|
Enable tracing at the debug level of XMLRPC requests and responses if requested.
|
|
|
|
=cut
|
|
|
|
if ($debug) {
|
|
$proxy->import(+trace => 'debug');
|
|
}
|
|
|
|
=head2 Checking Bugzilla's version
|
|
|
|
To make sure the Bugzilla you're connecting to supports the methods you wish to
|
|
call, you may want to compare the result of C<Bugzilla.version> to the
|
|
minimum required version your application needs.
|
|
|
|
=cut
|
|
|
|
$soapresult = $proxy->call('Bugzilla.version');
|
|
_die_on_fault($soapresult);
|
|
print 'Connecting to a Bugzilla of version ' . $soapresult->result()->{version} . ".\n";
|
|
|
|
=head2 Checking Bugzilla's timezone
|
|
|
|
To make sure that you understand the dates and times that Bugzilla returns to you, you may want to call C<Bugzilla.timezone>.
|
|
|
|
=cut
|
|
|
|
$soapresult = $proxy->call('Bugzilla.timezone');
|
|
_die_on_fault($soapresult);
|
|
print 'Bugzilla\'s timezone is ' . $soapresult->result()->{timezone} . ".\n";
|
|
|
|
=head2 Logging In and Out
|
|
|
|
=head3 Using Bugzilla's Environment Authentication
|
|
|
|
Use a
|
|
C<http://login:password@your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>
|
|
style URI.
|
|
You don't log out if you're using this kind of authentication.
|
|
|
|
=head3 Using Bugzilla's CGI Variable Authentication
|
|
|
|
Use the C<User.login> and C<User.logout> calls to log in and out, as shown
|
|
in this script.
|
|
|
|
The C<Bugzilla_restrictlogin> parameter is optional.
|
|
If omitted, Bugzilla's defaults apply (as specified by its C<restrictlogin>
|
|
parameter).
|
|
|
|
=cut
|
|
|
|
if (defined($Bugzilla_login)) {
|
|
if ($Bugzilla_login ne '') {
|
|
# Log in.
|
|
$soapresult = $proxy->call('User.login',
|
|
{ login => $Bugzilla_login,
|
|
password => $Bugzilla_password,
|
|
restrict_login => $Bugzilla_restrict } );
|
|
$Bugzilla_token = $soapresult->result->{token};
|
|
_die_on_fault($soapresult);
|
|
print "Login successful.\n";
|
|
}
|
|
else {
|
|
# Log out.
|
|
$soapresult = $proxy->call('User.logout');
|
|
_die_on_fault($soapresult);
|
|
print "Logout successful.\n";
|
|
}
|
|
}
|
|
|
|
=head2 Getting Extension Information
|
|
|
|
Returns all the information any extensions have decided to provide to the webservice.
|
|
|
|
=cut
|
|
|
|
if ($fetch_extension_info) {
|
|
$soapresult = $proxy->call('Bugzilla.extensions', {token => $Bugzilla_token});
|
|
_die_on_fault($soapresult);
|
|
my $extensions = $soapresult->result()->{extensions};
|
|
foreach my $extensionname (keys(%$extensions)) {
|
|
print "Extension '$extensionname' information\n";
|
|
my $extension = $extensions->{$extensionname};
|
|
foreach my $data (keys(%$extension)) {
|
|
print ' ' . $data . ' => ' . $extension->{$data} . "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
=head2 Retrieving Bug Information
|
|
|
|
Call C<Bug.get> with the ID of the bug you want to know more of.
|
|
The call will return a C<Bugzilla::Bug> object.
|
|
|
|
=cut
|
|
|
|
if ($bug_id) {
|
|
$soapresult = $proxy->call('Bug.get', { ids => [$bug_id], token => $Bugzilla_token});
|
|
_die_on_fault($soapresult);
|
|
$result = $soapresult->result;
|
|
my $bug = $result->{bugs}->[0];
|
|
foreach my $field (keys(%$bug)) {
|
|
my $value = $bug->{$field};
|
|
if (ref($value) eq 'HASH') {
|
|
foreach (keys %$value) {
|
|
print "$_: " . $value->{$_} . "\n";
|
|
}
|
|
}
|
|
else {
|
|
print "$field: $value\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
=head2 Retrieving Product Information
|
|
|
|
Call C<Product.get> with the name of the product you want to know more of.
|
|
The call will return a C<Bugzilla::Product> object.
|
|
|
|
=cut
|
|
|
|
if ($product_name) {
|
|
$soapresult = $proxy->call('Product.get', {'names' => [$product_name], token => $Bugzilla_token});
|
|
_die_on_fault($soapresult);
|
|
$result = $soapresult->result()->{'products'}->[0];
|
|
|
|
# Iterate all entries, the values may be scalars or array refs with hash refs.
|
|
foreach my $key (sort(keys %$result)) {
|
|
my $value = $result->{$key};
|
|
|
|
if (ref($value)) {
|
|
my $counter = 0;
|
|
foreach my $hash (@$value) {
|
|
while (my ($innerKey, $innerValue) = each %$hash) {
|
|
print "$key.$counter.$innerKey: $innerValue\n";
|
|
}
|
|
++$counter;
|
|
}
|
|
}
|
|
else {
|
|
print "$key: $value\n"
|
|
}
|
|
}
|
|
}
|
|
|
|
=head2 Creating A Bug
|
|
|
|
Call C<Bug.create> with the settings read from the file indicated on
|
|
the command line. The file must contain a valid anonymous hash to use
|
|
as argument for the call to C<Bug.create>.
|
|
The call will return a hash with a bug id for the newly created bug.
|
|
|
|
=cut
|
|
|
|
if ($create_file_name) {
|
|
my $bug_fields = do "$create_file_name";
|
|
$bug_fields->{Bugzilla_token} = $Bugzilla_token;
|
|
$soapresult = $proxy->call('Bug.create', \%$bug_fields);
|
|
_die_on_fault($soapresult);
|
|
$result = $soapresult->result;
|
|
|
|
if (ref($result) eq 'HASH') {
|
|
foreach (keys(%$result)) {
|
|
print "$_: $$result{$_}\n";
|
|
}
|
|
}
|
|
else {
|
|
print "$result\n";
|
|
}
|
|
|
|
}
|
|
|
|
=head2 Getting Legal Field Values
|
|
|
|
Call C<Bug.legal_values> with the name of the field (including custom
|
|
select fields). The call will return a reference to an array with the
|
|
list of legal values for this field.
|
|
|
|
=cut
|
|
|
|
if ($legal_field_values) {
|
|
$soapresult = $proxy->call('Bug.legal_values', {field => $legal_field_values, token => $Bugzilla_token} );
|
|
_die_on_fault($soapresult);
|
|
$result = $soapresult->result;
|
|
|
|
print join("\n", @{$result->{values}}) . "\n";
|
|
}
|
|
|
|
=head2 Adding a comment to a bug
|
|
|
|
Call C<Bug.add_comment> with the bug id, the comment text, and optionally the number
|
|
of hours you worked on the bug, and a boolean indicating if the comment is private
|
|
or not.
|
|
|
|
=cut
|
|
|
|
if ($add_comment) {
|
|
if ($bug_id) {
|
|
$soapresult = $proxy->call('Bug.add_comment', {id => $bug_id,
|
|
comment => $add_comment, private => $private, work_time => $work_time, token => $Bugzilla_token});
|
|
_die_on_fault($soapresult);
|
|
print "Comment added.\n";
|
|
}
|
|
else {
|
|
print "A --bug_id must be supplied to add a comment.";
|
|
}
|
|
}
|
|
|
|
=head1 NOTES
|
|
|
|
=head2 Character Set Encoding
|
|
|
|
Make sure that your application either uses the same character set
|
|
encoding as Bugzilla does, or that it converts correspondingly when using the
|
|
web service API.
|
|
By default, Bugzilla uses UTF-8 as its character set encoding.
|
|
|
|
=head2 Format For Create File
|
|
|
|
The create format file is a piece of Perl code, that should look something like
|
|
this:
|
|
|
|
{
|
|
product => "TestProduct",
|
|
component => "TestComponent",
|
|
summary => "TestBug - created from bz_webservice_demo.pl",
|
|
version => "unspecified",
|
|
description => "This is a description of the bug... hohoho",
|
|
op_sys => "All",
|
|
platform => "All",
|
|
priority => "P4",
|
|
severity => "normal"
|
|
};
|
|
|
|
=head1 SEE ALSO
|
|
|
|
There are code comments in C<bz_webservice_demo.pl> which might be of further
|
|
help to you.
|
|
|
|
=cut
|
|
|
|
sub _die_on_fault {
|
|
my $soapresult = shift;
|
|
|
|
if ($soapresult->fault) {
|
|
my ($package, $filename, $line) = caller;
|
|
die $soapresult->faultcode . ' ' . $soapresult->faultstring .
|
|
" in SOAP call near $filename line $line.\n";
|
|
}
|
|
}
|
|
|
|
sub _syntaxhelp {
|
|
my $msg = shift;
|
|
|
|
print "Error: $msg\n";
|
|
pod2usage({'-verbose' => 0, '-exitval' => 1});
|
|
}
|