bz_webservice_demo.pl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #!/usr/bin/env perl -w
  2. # -*- Mode: perl; indent-tabs-mode: nil -*-
  3. #
  4. # The contents of this file are subject to the Mozilla Public
  5. # License Version 1.1 (the “License”); you may not use this file
  6. # except in compliance with the License. You may obtain a copy of
  7. # the License at http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an “AS
  10. # IS” basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. # implied. See the License for the specific language governing
  12. # rights and limitations under the License.
  13. #
  14. # The Original Code is the Bugzilla Bug Tracking System.
  15. #
  16. # Contributor(s): Marc Schumann <wurblzap@gmail.com>
  17. # Mads Bondo Dydensborg <mbd@dbc.dk>
  18. =head1 NAME
  19. bz_webservice_demo.pl - Show how to talk to Bugzilla via XMLRPC
  20. =head1 SYNOPSIS
  21. C<bz_webservice_demo.pl [options]>
  22. C<bz_webservice_demo.pl --help> for detailed help
  23. =cut
  24. use strict;
  25. use lib qw(lib);
  26. use Getopt::Long;
  27. use Pod::Usage;
  28. use File::Basename qw(dirname);
  29. use File::Spec;
  30. use HTTP::Cookies;
  31. use XMLRPC::Lite;
  32. # If you want, say “use Bugzilla::WebService::Constants” here to get access
  33. # to Bugzilla's web service error code constants.
  34. # If you do this, remember to issue a “use lib” pointing to your Bugzilla
  35. # installation directory, too.
  36. my $help;
  37. my $Bugzilla_uri;
  38. my $Bugzilla_login;
  39. my $Bugzilla_password;
  40. my $Bugzilla_remember;
  41. my $bug_id;
  42. my $product_name;
  43. my $create_file_name;
  44. my $legal_field_values;
  45. my $add_comment;
  46. my $private;
  47. my $work_time;
  48. my $fetch_extension_info = 0;
  49. GetOptions('help|h|?' => \$help,
  50. 'uri=s' => \$Bugzilla_uri,
  51. 'login:s' => \$Bugzilla_login,
  52. 'password=s' => \$Bugzilla_password,
  53. 'rememberlogin!' => \$Bugzilla_remember,
  54. 'bug_id:s' => \$bug_id,
  55. 'product_name:s' => \$product_name,
  56. 'create:s' => \$create_file_name,
  57. 'field:s' => \$legal_field_values,
  58. 'comment:s' => \$add_comment,
  59. 'private:i' => \$private,
  60. 'worktime:f' => \$work_time,
  61. 'extension_info' => \$fetch_extension_info
  62. ) or pod2usage({'-verbose' => 0, '-exitval' => 1});
  63. =head1 OPTIONS
  64. =over
  65. =item --help, -h, -?
  66. Print a short help message and exit.
  67. =item --uri
  68. URI to Bugzilla's C<xmlrpc.cgi> script, along the lines of
  69. C<http://your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>.
  70. =item --login
  71. Bugzilla login name. Specify this together with B<--password> in order to log in.
  72. Specify this without a value in order to log out.
  73. =item --password
  74. Bugzilla password. Specify this together with B<--login> in order to log in.
  75. =item --rememberlogin
  76. Gives access to Bugzilla's "Bugzilla_remember" option.
  77. Specify this option while logging in to do the same thing as ticking the
  78. C<Bugzilla_remember> box on Bugilla's log in form.
  79. Don't specify this option to do the same thing as unchecking the box.
  80. See Bugzilla's rememberlogin parameter for details.
  81. =item --bug_id
  82. Pass a bug ID to have C<bz_webservice_demo.pl> do some bug-related test calls.
  83. =item --product_name
  84. Pass a product name to have C<bz_webservice_demo.pl> do some product-related
  85. test calls.
  86. =item --create
  87. Specify a file that contains settings for the creating of a new bug.
  88. =item --field
  89. Pass a field name to get legal values for this field. It must be either a
  90. global select field (such as bug_status, resolution, rep_platform, op_sys,
  91. priority, bug_severity) or a custom select field.
  92. =item --comment
  93. A comment to add to a bug identified by B<--bug_id>. You must also pass a B<--login>
  94. and B<--password> to log in to Bugzilla.
  95. =item --private
  96. An optional non-zero value to specify B<--comment> as private.
  97. =item --worktime
  98. An optional double precision number specifying the work time for B<--comment>.
  99. =item --extension_info
  100. If specified on the command line, the script returns the information about the
  101. extensions that are installed.
  102. =back
  103. =head1 DESCRIPTION
  104. =cut
  105. pod2usage({'-verbose' => 1, '-exitval' => 0}) if $help;
  106. _syntaxhelp('URI unspecified') unless $Bugzilla_uri;
  107. # We will use this variable for SOAP call results.
  108. my $soapresult;
  109. # We will use this variable for function call results.
  110. my $result;
  111. # Open our cookie jar. We save it into a file so that we may re-use cookies
  112. # to avoid the need of logging in every time. You're encouraged, but not
  113. # required, to do this in your applications, too.
  114. # Cookies are only saved if Bugzilla's rememberlogin parameter is set to one of
  115. # - on
  116. # - defaulton (and you didn't pass 0 as third parameter to User.login)
  117. # - defaultoff (and you passed 1 as third parameter to User.login)
  118. my $cookie_jar =
  119. new HTTP::Cookies('file' => File::Spec->catdir(dirname($0), 'cookies.txt'),
  120. 'autosave' => 1);
  121. =head2 Initialization
  122. Using the XMLRPC::Lite class, you set up a proxy, as shown in this script.
  123. Bugzilla's XMLRPC URI ends in C<xmlrpc.cgi>, so your URI looks along the lines
  124. of C<http://your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>.
  125. =cut
  126. my $proxy = XMLRPC::Lite->proxy($Bugzilla_uri,
  127. 'cookie_jar' => $cookie_jar);
  128. =head2 Checking Bugzilla's version
  129. To make sure the Bugzilla you're connecting to supports the methods you wish to
  130. call, you may want to compare the result of C<Bugzilla.version> to the
  131. minimum required version your application needs.
  132. =cut
  133. $soapresult = $proxy->call('Bugzilla.version');
  134. _die_on_fault($soapresult);
  135. print 'Connecting to a Bugzilla of version ' . $soapresult->result()->{version} . ".\n";
  136. =head2 Checking Bugzilla's timezone
  137. To make sure that you understand the dates and times that Bugzilla returns to you, you may want to call C<Bugzilla.timezone>.
  138. =cut
  139. $soapresult = $proxy->call('Bugzilla.timezone');
  140. _die_on_fault($soapresult);
  141. print 'Bugzilla\'s timezone is ' . $soapresult->result()->{timezone} . ".\n";
  142. =head2 Getting Extension Information
  143. Returns all the information any extensions have decided to provide to the webservice.
  144. =cut
  145. if ($fetch_extension_info) {
  146. $soapresult = $proxy->call('Bugzilla.extensions');
  147. _die_on_fault($soapresult);
  148. my $extensions = $soapresult->result()->{extensions};
  149. foreach my $extensionname (keys(%$extensions)) {
  150. print "Extensionn '$extensionname' information\n";
  151. my $extension = $extensions->{$extensionname};
  152. foreach my $data (keys(%$extension)) {
  153. print ' ' . $data . ' => ' . $extension->{$data} . "\n";
  154. }
  155. }
  156. }
  157. =head2 Logging In and Out
  158. =head3 Using Bugzilla's Environment Authentication
  159. Use a
  160. C<http://login:password@your.bugzilla.installation/path/to/bugzilla/xmlrpc.cgi>
  161. style URI.
  162. You don't log out if you're using this kind of authentication.
  163. =head3 Using Bugzilla's CGI Variable Authentication
  164. Use the C<User.login> and C<User.logout> calls to log in and out, as shown
  165. in this script.
  166. The C<Bugzilla_remember> parameter is optional.
  167. If omitted, Bugzilla's defaults apply (as specified by its C<rememberlogin>
  168. parameter).
  169. Bugzilla hands back cookies you'll need to pass along during your work calls.
  170. =cut
  171. if (defined($Bugzilla_login)) {
  172. if ($Bugzilla_login ne '') {
  173. # Log in.
  174. $soapresult = $proxy->call('User.login',
  175. { login => $Bugzilla_login,
  176. password => $Bugzilla_password,
  177. remember => $Bugzilla_remember } );
  178. _die_on_fault($soapresult);
  179. print "Login successful.\n";
  180. }
  181. else {
  182. # Log out.
  183. $soapresult = $proxy->call('User.logout');
  184. _die_on_fault($soapresult);
  185. print "Logout successful.\n";
  186. }
  187. }
  188. =head2 Retrieving Bug Information
  189. Call C<Bug.get> with the ID of the bug you want to know more of.
  190. The call will return a C<Bugzilla::Bug> object.
  191. Note: You can also use "Bug.get_bugs" for compatibility with Bugzilla 3.0 API.
  192. =cut
  193. if ($bug_id) {
  194. $soapresult = $proxy->call('Bug.get', { ids => [$bug_id] });
  195. _die_on_fault($soapresult);
  196. $result = $soapresult->result;
  197. my $bug = $result->{bugs}->[0];
  198. foreach my $field (keys(%$bug)) {
  199. my $value = $bug->{$field};
  200. if (ref($value) eq 'HASH') {
  201. foreach (keys %$value) {
  202. print "$_: " . $value->{$_} . "\n";
  203. }
  204. }
  205. else {
  206. print "$field: $value\n";
  207. }
  208. }
  209. }
  210. =head2 Retrieving Product Information
  211. Call C<Product.get_product> with the name of the product you want to know more
  212. of.
  213. The call will return a C<Bugzilla::Product> object.
  214. =cut
  215. if ($product_name) {
  216. $soapresult = $proxy->call('Product.get_product', $product_name);
  217. _die_on_fault($soapresult);
  218. $result = $soapresult->result;
  219. if (ref($result) eq 'HASH') {
  220. foreach (keys(%$result)) {
  221. print "$_: $$result{$_}\n";
  222. }
  223. }
  224. else {
  225. print "$result\n";
  226. }
  227. }
  228. =head2 Creating A Bug
  229. Call C<Bug.create> with the settings read from the file indicated on
  230. the command line. The file must contain a valid anonymous hash to use
  231. as argument for the call to C<Bug.create>.
  232. The call will return a hash with a bug id for the newly created bug.
  233. =cut
  234. if ($create_file_name) {
  235. $soapresult = $proxy->call('Bug.create', do "$create_file_name" );
  236. _die_on_fault($soapresult);
  237. $result = $soapresult->result;
  238. if (ref($result) eq 'HASH') {
  239. foreach (keys(%$result)) {
  240. print "$_: $$result{$_}\n";
  241. }
  242. }
  243. else {
  244. print "$result\n";
  245. }
  246. }
  247. =head2 Getting Legal Field Values
  248. Call C<Bug.legal_values> with the name of the field (including custom
  249. select fields). The call will return a reference to an array with the
  250. list of legal values for this field.
  251. =cut
  252. if ($legal_field_values) {
  253. $soapresult = $proxy->call('Bug.legal_values', {field => $legal_field_values} );
  254. _die_on_fault($soapresult);
  255. $result = $soapresult->result;
  256. print join("\n", @{$result->{values}}) . "\n";
  257. }
  258. =head2 Adding a comment to a bug
  259. Call C<Bug.add_comment> with the bug id, the comment text, and optionally the number
  260. of hours you worked on the bug, and a boolean indicating if the comment is private
  261. or not.
  262. =cut
  263. if ($add_comment) {
  264. if ($bug_id) {
  265. $soapresult = $proxy->call('Bug.add_comment', {id => $bug_id,
  266. comment => $add_comment, private => $private, work_time => $work_time});
  267. _die_on_fault($soapresult);
  268. print "Comment added.\n";
  269. }
  270. else {
  271. print "A --bug_id must be supplied to add a comment.";
  272. }
  273. }
  274. =head1 NOTES
  275. =head2 Character Set Encoding
  276. Make sure that your application either uses the same character set
  277. encoding as Bugzilla does, or that it converts correspondingly when using the
  278. web service API.
  279. By default, Bugzilla uses UTF-8 as its character set encoding.
  280. =head2 Format For Create File
  281. The create format file is a piece of Perl code, that should look something like
  282. this:
  283. {
  284. product => "TestProduct",
  285. component => "TestComponent",
  286. summary => "TestBug - created from bz_webservice_demo.pl",
  287. version => "unspecified",
  288. description => "This is a description of the bug... hohoho",
  289. op_sys => "All",
  290. platform => "All",
  291. priority => "P4",
  292. severity => "normal"
  293. };
  294. =head1 SEE ALSO
  295. There are code comments in C<bz_webservice_demo.pl> which might be of further
  296. help to you.
  297. =cut
  298. sub _die_on_fault {
  299. my $soapresult = shift;
  300. if ($soapresult->fault) {
  301. my ($package, $filename, $line) = caller;
  302. die $soapresult->faultcode . ' ' . $soapresult->faultstring .
  303. " in SOAP call near $filename line $line.\n";
  304. }
  305. }
  306. sub _syntaxhelp {
  307. my $msg = shift;
  308. print "Error: $msg\n";
  309. pod2usage({'-verbose' => 0, '-exitval' => 1});
  310. }