ruby-bugzilla 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/bugzilla.rb ADDED
@@ -0,0 +1,787 @@
1
+ # bugzilla.rb
2
+ # Copyright (C) 2010 Red Hat, Inc.
3
+ #
4
+ # Authors:
5
+ # Akira TAGOH <tagoh@redhat.com>
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 59 Temple Place - Suite 330,
20
+ # Boston, MA 02111-1307, USA.
21
+
22
+ require 'xmlrpc/client'
23
+
24
+
25
+ =begin rdoc
26
+
27
+ == Bugzilla
28
+
29
+ =end
30
+
31
+ module Bugzilla
32
+
33
+ VERSION = "0.1"
34
+
35
+ =begin rdoc
36
+
37
+ === Bugzilla::Plugin
38
+
39
+ =end
40
+
41
+ module Plugin
42
+
43
+ =begin rdoc
44
+
45
+ ==== Bugzilla::Plugin::Template
46
+
47
+ =end
48
+
49
+ class Template
50
+ @@plugins = []
51
+
52
+ def initialize
53
+ @hostname = nil
54
+ end # def initialize
55
+
56
+ attr_reader :hostname
57
+
58
+ def Template.inherited(subclass)
59
+ @@plugins << subclass
60
+ end # def inherited
61
+
62
+ def run(hook, host, *args)
63
+ @@plugins.each do |k|
64
+ i = k.new
65
+ if i.hostname == host || host.nil? then
66
+ case hook
67
+ when :parser
68
+ i.parserhook(*args)
69
+ when :pre
70
+ i.prehook(*args)
71
+ when :post
72
+ i.posthook(*args)
73
+ else
74
+ end
75
+ end
76
+ end
77
+ end # def run
78
+
79
+ def parserhook(parser, argv, opts)
80
+ end # def parserhook
81
+
82
+ def prehook(cmd, opts)
83
+ end # def prehook
84
+
85
+ def posthook(cmd, opts)
86
+ end # def posthook
87
+
88
+ end # class Template
89
+
90
+ end # module Plugin
91
+
92
+ =begin rdoc
93
+
94
+ === Bugzilla::XMLRPC
95
+
96
+ =end
97
+
98
+ class XMLRPC
99
+
100
+ =begin rdoc
101
+
102
+ ==== Bugzilla::XMLRPC#new(server, port = 443)
103
+
104
+ =end
105
+
106
+ def initialize(server, port = 443)
107
+ use_ssl = port == 443 ? true : false
108
+ @xmlrpc = ::XMLRPC::Client.new(server, '/xmlrpc.cgi', port, nil, nil, nil, nil, use_ssl, 60)
109
+ end # def initialize
110
+
111
+ =begin rdoc
112
+
113
+ ==== Bugzilla::XMLRPC#call(cmd, params, user = nil, password = nil)
114
+
115
+ =end
116
+
117
+ def call(cmd, params = {}, user = nil, password = nil)
118
+ params = {} if params.nil?
119
+ params['Bugzilla_login'] = user unless user.nil? || password.nil?
120
+ params['Bugzilla_password'] = password unless user.nil? || password.nil?
121
+ @xmlrpc.call(cmd, params)
122
+ end # def call
123
+
124
+ end # class XMLRPC
125
+
126
+ =begin rdoc
127
+
128
+ === Bugzilla::Skeleton
129
+
130
+ =end
131
+
132
+ class Skeleton
133
+
134
+ def initialize(iface)
135
+ @iface = iface
136
+ end # def initialize
137
+
138
+ def method_missing(symbol, *args)
139
+ m = "_#{symbol}"
140
+ klass = self.class.to_s.sub(/\ABugzilla::/, '')
141
+ fm = "#{klass}.#{symbol}"
142
+ if self.respond_to?(m) then
143
+ __send__(m, fm, *args)
144
+ else
145
+ raise NoMethodError, sprintf("No such Bugzilla APIs: %s.%s", klass, symbol)
146
+ end
147
+ end # def method_missing
148
+
149
+ end # class Skeleton
150
+
151
+ =begin rdoc
152
+
153
+ === Bugzilla::Bugzilla
154
+
155
+ Bugzilla::Bugzilla class is to access the
156
+ Bugzilla::WebService::Bugzilla API that provides functions
157
+ tell you about Bugzilla in general.
158
+
159
+ =end
160
+
161
+ class Bugzilla < Skeleton
162
+
163
+ =begin rdoc
164
+
165
+ ==== Bugzilla::Bugzilla#check_version(version_)
166
+
167
+ Returns Array contains the result of the version check and
168
+ Bugzilla version that is running on.
169
+
170
+ =end
171
+
172
+ def check_version(version_)
173
+ v = version
174
+ f = false
175
+ if v.kind_of?(Hash) && v.include?("version") &&
176
+ v['version'] >= "#{version_}" then
177
+ f = true
178
+ end
179
+
180
+ [f, v['version']]
181
+ end # def check_version
182
+
183
+ =begin rdoc
184
+
185
+ ==== Bugzilla::Bugzilla#requires_version(cmd, version_)
186
+
187
+ Raise an exception if the Bugzilla doesn't satisfy
188
+ the requirement of the _version_.
189
+
190
+ =end
191
+
192
+ def requires_version(cmd, version_)
193
+ v = check_version(version_)
194
+ raise NoMethodError, sprintf("%s is not supported in Bugzilla %s", cmd, v[1]) unless v[0]
195
+ end # def requires_version
196
+
197
+ =begin rdoc
198
+
199
+ ==== Bugzilla::Bugzilla#version
200
+
201
+ Raw Bugzilla API to obtain the Bugzilla version.
202
+
203
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bugzilla.html
204
+
205
+ =end
206
+
207
+ =begin rdoc
208
+
209
+ ==== Bugzilla::Bugzilla#extensions
210
+
211
+ Raw Bugzilla API to obtain the information about
212
+ the extensions that are currently installed and enabled in
213
+ the Bugzilla.
214
+
215
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bugzilla.html
216
+
217
+ =end
218
+
219
+ =begin rdoc
220
+
221
+ ==== Bugzilla::Bugzilla#timezone
222
+
223
+ Raw Bugzilla API to obtain the timezone that Bugzilla
224
+ expects dates and times in.
225
+
226
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bugzilla.html
227
+
228
+ =end
229
+
230
+ =begin rdoc
231
+
232
+ ==== Bugzilla::Bugzilla#time
233
+
234
+ Raw Bugzilla API to obtain the information about what time
235
+ the bugzilla server thinks it is, and what timezone it's
236
+ running on.
237
+
238
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bugzilla.html
239
+
240
+ =end
241
+
242
+ protected
243
+
244
+ def _version(cmd, *args)
245
+ @iface.call(cmd)
246
+ end # def _version
247
+
248
+ def _extensions(cmd, *args)
249
+ requires_version(cmd, 3.2)
250
+
251
+ @iface.call(cmd)
252
+ end # def _extensions
253
+
254
+ def _timezone(cmd, *args)
255
+ @iface.call(cmd)
256
+ end # def _timezone
257
+
258
+ def _time(cmd, *args)
259
+ requires_version(cmd, 3.4)
260
+
261
+ @iface.call(cmd)
262
+ end # def _time
263
+
264
+ end # class Bugzilla
265
+
266
+ =begin rdoc
267
+
268
+ === Bugzilla::APITemplate
269
+
270
+ =end
271
+
272
+ class APITemplate < Skeleton
273
+
274
+ def initialize(iface)
275
+ super
276
+
277
+ @bz = Bugzilla.new(iface)
278
+ end # def initialize
279
+
280
+ def method_missing(symbol, *args)
281
+ if @bz.respond_to?(symbol) then
282
+ @bz.__send__(symbol, *args)
283
+ else
284
+ super
285
+ end
286
+ end # def method_missing
287
+
288
+ end # class APITemplate
289
+
290
+ =begin rdoc
291
+
292
+ === Bugzilla::Product
293
+
294
+ Bugzilla::Product class is to access
295
+ the Bugzilla::WebService::Product API that allows you to
296
+ list the available Products and get information about them.
297
+
298
+ =end
299
+
300
+ class Product < APITemplate
301
+
302
+ =begin rdoc
303
+
304
+ ==== Bugzilla::Product#selectable_products
305
+
306
+ Returns the products that the user can search on as Hash
307
+ contains the product name as the Hash key and Array as the
308
+ value. Array contains the list of _id_, _name_,
309
+ _description_ and _internals_ according to API documentation
310
+ though, actually the component, the version and the target
311
+ milestone.
312
+
313
+ =end
314
+
315
+ def selectable_products
316
+ ids = get_selectable_products
317
+ get(ids)
318
+ end # def selectable_products
319
+
320
+ =begin rdoc
321
+
322
+ ==== Bugzilla::Product#enterable_products
323
+
324
+ Returns the products that the user can enter bugs against
325
+ as Hash contains the product name as the Hash key and Array
326
+ as the value. Array contains the list of _id_, _name_,
327
+ _description_ and _internals_ according to API documentation
328
+ though, actually the component, the version and the target
329
+ milestone.
330
+
331
+ =end
332
+
333
+ def enterable_products
334
+ ids = get_enterable_products
335
+ get(ids)
336
+ end # def enterable_products
337
+
338
+ =begin rdoc
339
+
340
+ ==== Bugzilla::Product#accessible_products
341
+
342
+ Returns the products that the user can search or enter bugs
343
+ against as Hash contains the product name as the Hash key
344
+ and Array as the value. Array contains the list of _id_,
345
+ _name_, _description_ and _internals_ according to API
346
+ documentation though, actually the component, the version
347
+ and the target milestone.
348
+
349
+ =end
350
+
351
+ def accessible_products
352
+ ids = get_accessible_products
353
+ get(ids)
354
+ end # def accessible_products
355
+
356
+ =begin rdoc
357
+
358
+ ==== Bugzilla::Product#get_selectable_products
359
+
360
+ Raw Bugzilla API to obtain the products that the user can
361
+ search on.
362
+
363
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Product.html
364
+
365
+ =end
366
+
367
+ =begin rdoc
368
+
369
+ ==== Bugzilla::Product#get_enterable_products
370
+
371
+ Raw Bugzilla API to obtain the products that the user can
372
+ enter bugs against.
373
+
374
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Product.html
375
+
376
+ =end
377
+
378
+ =begin rdoc
379
+
380
+ ==== Bugzilla::Product#get_accessible_products
381
+
382
+ Raw Bugzilla API to obtain the products that the user can
383
+ search or enter bugs against.
384
+
385
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Product.html
386
+
387
+ =end
388
+
389
+ protected
390
+
391
+ def _get_selectable_products(cmd, *args)
392
+ @iface.call(cmd)
393
+ end # def _get_selectable_products
394
+
395
+ def _get_enterable_products(cmd, *args)
396
+ @iface.call(cmd)
397
+ end # def _get_entrable_products
398
+
399
+ def _get_accessible_products(cmd, *args)
400
+ @iface.call(cmd)
401
+ end # def _get_accessible_products
402
+
403
+ def _get(cmd, ids, *args)
404
+ params = {}
405
+
406
+ if ids.kind_of?(Hash) then
407
+ raise ArgumentError, sprintf("Invalid parameter: %s", ids.inspect) unless ids.include?('ids')
408
+ params[:ids] = ids['ids']
409
+ elsif ids.kind_of?(Array) then
410
+ params[:ids] = ids
411
+ else
412
+ params[:ids] = [ids]
413
+ end
414
+
415
+ @iface.call(cmd, params)
416
+ end # def _get
417
+
418
+ end # class Product
419
+
420
+ =begin rdoc
421
+
422
+ === Bugzilla::Bug
423
+
424
+ Bugzilla::Bug class is to access
425
+ the Bugzilla::WebService::Bug API that allows you to file
426
+ a new bug in Bugzilla or get information about bugs that
427
+ have already been filed.
428
+
429
+ =end
430
+
431
+ class Bug < APITemplate
432
+
433
+ FIELDS_SUMMARY = ["id", "product", "component", "status", "severity", "summary"]
434
+ FIELDS_DETAILS = FIELDS_SUMMARY + ["assigned_to", "internals", "priority", "resolution"]
435
+ FIELDS_ALL = ["alias", "assigned_to", "component",
436
+ "creation_time", "dupe_of",
437
+ "external_bugs", "groups", "id",
438
+ "internals", "is_open",
439
+ "last_change_time", "priority", "product",
440
+ "resolution", "severity", "status",
441
+ "summary"]
442
+
443
+ =begin rdoc
444
+
445
+ ==== Bugzilla::Bug#get_bugs(bugs, fields = Bugzilla::Bug::FIELDS_SUMMARY)
446
+
447
+ Get the _bugs_ information from Bugzilla. either of String
448
+ or Numeric or Array would be acceptable for _bugs_. you can
449
+ specify the fields you want to look up with _fields_.
450
+
451
+ FWIW this name conflicts to Bugzilla API but this isn's a
452
+ primitive method since get_bugs method in WebService API is
453
+ actually deprecated.
454
+
455
+ =end
456
+
457
+ def get_bugs(bugs, fields = ::Bugzilla::Bug::FIELDS_SUMMARY)
458
+ params = {}
459
+
460
+ if bugs.kind_of?(Array) then
461
+ params['ids'] = bugs
462
+ elsif bugs.kind_of?(Integer) ||
463
+ bugs.kind_of?(String) then
464
+ params['ids'] = [bugs]
465
+ else
466
+ raise ArgumentError, sprintf("Unknown type of arguments: %s", bugs.class)
467
+ end
468
+ unless fields.nil? then
469
+ unless (fields - ::Bugzilla::Bug::FIELDS_ALL).empty? then
470
+ raise ArgumentError, sprintf("Invalid fields: %s", (::Bugzilla::Bug::FIELDS_ALL - fields).join(' '))
471
+ end
472
+ params['include_fields'] = fields
473
+ end
474
+
475
+ result = get(params)
476
+
477
+ if fields.nil? || fields == ::Bugzilla::Bug::FIELDS_ALL then
478
+ get_comments(bugs).each do |id, c|
479
+ result['bugs'].each do |r|
480
+ if r['id'].to_s == id then
481
+ r['comments'] = c['comments']
482
+ r['comments'] = [] if r['comments'].nil?
483
+ break
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ # 'bugs' is only in interests.
490
+ # XXX: need to deal with 'faults' ?
491
+ result['bugs']
492
+ end # def get_bugs
493
+
494
+ =begin rdoc
495
+
496
+ ==== Bugzilla::Bug#get_comments(bugs)
497
+
498
+ =end
499
+
500
+ def get_comments(bugs)
501
+ params = {}
502
+
503
+ if bugs.kind_of?(Array) then
504
+ params['ids'] = bugs
505
+ elsif bugs.kind_of?(Integer) ||
506
+ bugs.kind_of?(String) then
507
+ params['ids'] = [bugs]
508
+ else
509
+ raise ArgumentError, sprintf("Unknown type of arguments: %s", bugs.class)
510
+ end
511
+
512
+ result = comments(params)
513
+
514
+ # not supporting comment_ids. so drop "comments".
515
+ result['bugs']
516
+ end # def get_comments
517
+
518
+ =begin rdoc
519
+
520
+ ==== Bugzilla::Bug#fields(params)
521
+
522
+ Raw Bugzilla API to obtain the information about valid bug
523
+ fields, including the lists of legal values for each field.
524
+
525
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bug.html
526
+
527
+ =end
528
+
529
+ =begin rdoc
530
+
531
+ ==== Bugzilla::Bug#legal_values(params)
532
+
533
+ Raw Bugzilla API to obtain the information what values are
534
+ allowed for a particular field.
535
+
536
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bug.html
537
+
538
+ =end
539
+
540
+ =begin rdoc
541
+
542
+ ==== Bugzilla::Bug#attachments(params)
543
+
544
+ Raw Bugzilla API to obtain the information about
545
+ attachments, given a list of bugs and/or attachment ids.
546
+
547
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bug.html
548
+
549
+ =end
550
+
551
+ =begin rdoc
552
+
553
+ ==== Bugzilla::Bug#comments(params)
554
+
555
+ Raw Bugzilla API to obtain the information about comments,
556
+ given a list of bugs and/or comment ids.
557
+
558
+ See http://www.bugzilla.org/docs/tip/en/html/api/Bugzilla/WebService/Bug.html
559
+
560
+ =end
561
+
562
+ =begin rdoc
563
+
564
+ ==== Bugzilla::Bug#get(params)
565
+
566
+ Raw Bugzilla API to obtain the information about particular
567
+ bugs in the database.
568
+
569
+ =end
570
+
571
+ =begin rdoc
572
+
573
+ ==== Bugzilla::Bug#history(params)
574
+
575
+ Raw Bugzilla API to obtain the history of changes for
576
+ particular bugs in the database.
577
+
578
+ =end
579
+
580
+ =begin rdoc
581
+
582
+ ==== Bugzilla::Bug#search(params)
583
+
584
+ Raw Bugzilla API to search for bugs based on particular
585
+ criteria.
586
+
587
+ =end
588
+
589
+ protected
590
+
591
+ def _fields(cmd, *args)
592
+ requires_version(cmd, 3.6)
593
+ params = {}
594
+
595
+ if args[0].kind_of?(Array) then
596
+ x = args[0].map {|x| x.kind_of?(Integer)}.uniq
597
+ if x.length == 1 && x[0] then
598
+ params['ids'] = args[0]
599
+ else
600
+ x = args[0].map {|x| x.kind_of?(String)}.uniq
601
+ if x.length == 1 && x[0] then
602
+ params['names'] = args[0]
603
+ end
604
+ end
605
+ elsif args[0].kind_of?(Hash) then
606
+ params = args[0]
607
+ elsif args[0].kind_of?(Integer) then
608
+ params['ids'] = [args[0]]
609
+ elsif args[0].kind_of?(String) then
610
+ params['names'] = [args[0]]
611
+ elsif args[0].nil? then
612
+ else
613
+ raise ArgumentError, "Invalid parameters"
614
+ end
615
+
616
+ @iface.call(cmd, params)
617
+ end # def _fields
618
+
619
+ def _legal_values(cmd, *args)
620
+ raise ArgumentError, "Invalid parameters" unless args[0].kind_of?(Hash)
621
+
622
+ @iface.call(cmd, args[0])
623
+ end # def _legal_values
624
+
625
+ def _attachments(cmd, *args)
626
+ requires_version(cmd, 3.6)
627
+
628
+ raise ArgumentError, "Invalid parameters" unless args[0].kind_of?(Hash)
629
+
630
+ @iface.call(cmd, args[0])
631
+ end # def _attachments
632
+
633
+ def _comments(cmd, *args)
634
+ requires_version(cmd, 3.4)
635
+
636
+ raise ArgumentError, "Invalid parameters" unless args[0].kind_of?(Hash)
637
+
638
+ @iface.call(cmd, args[0])
639
+ end # def _comments
640
+
641
+ def _get(cmd, *args)
642
+ params = {}
643
+
644
+ if args[0].kind_of?(Hash) then
645
+ params = args[0]
646
+ elsif args[0].kind_of?(Array) then
647
+ params['ids'] = args[0]
648
+ elsif args[0].kind_of?(Integer) ||
649
+ args[0].kind_of?(String) then
650
+ params['ids'] = [args[0]]
651
+ else
652
+ raise ArgumentError, "Invalid parameters"
653
+ end
654
+ if check_version(3.4)[0] then
655
+ params['permissive'] = true
656
+ end
657
+
658
+ @iface.call(cmd, params)
659
+ end # def _get
660
+
661
+ def _history(cmd, *args)
662
+ requires_version(cmd, 3.4)
663
+
664
+ params = {}
665
+
666
+ if args[0].kind_of?(Hash) then
667
+ params = args[0]
668
+ elsif args[0].kind_of?(Array) then
669
+ params['ids'] = args[0]
670
+ elsif args[0].kind_of?(Integer) ||
671
+ args[0].kind_of?(String) then
672
+ params['ids'] = [args[0]]
673
+ else
674
+ raise ArgumentError, "Invalid parameters"
675
+ end
676
+
677
+ @iface.call(cmd, params)
678
+ end # def _history
679
+
680
+ def _search(cmd, *args)
681
+ requires_version(cmd, 3.4)
682
+
683
+ raise ArgumentError, "Invalid parameters" unless args[0].kind_of?(Hash)
684
+
685
+ @iface.call(cmd, args[0])
686
+ end # def _search
687
+
688
+ def __create(cmd, *args)
689
+ # FIXME
690
+ end # def _create
691
+
692
+ def __add_attachment(cmd, *args)
693
+ requires_version(cmd, 4.0)
694
+ # FIXME
695
+ end # def _add_attachment
696
+
697
+ def __add_comment(cmd, *args)
698
+ requires_version(cmd, 3.2)
699
+ # FIXME
700
+ end # def _add_comment
701
+
702
+ def __update(cmd, *args)
703
+ requires_version(cmd, 4.0)
704
+ # FIXME
705
+ end # def _update
706
+
707
+ def __update_see_also(cmd, *args)
708
+ requires_version(cmd, 3.4)
709
+ # FIXME
710
+ end # def _update_see_also
711
+
712
+ end # class Bug
713
+
714
+ =begin rdoc
715
+
716
+ === Bugzilla::User
717
+
718
+ Bugzilla::User class is to access the
719
+ Bugzilla::WebService::User API that allows you to create
720
+ User Accounts and log in/out using an existing account.
721
+
722
+ =end
723
+
724
+ class User < APITemplate
725
+
726
+ =begin rdoc
727
+
728
+ ==== Bugzilla::User#session(user, password)
729
+
730
+ Keeps the bugzilla session during doing something in the block.
731
+
732
+ =end
733
+
734
+ def session(user, password)
735
+ if user.nil? || password.nil? then
736
+ yield
737
+ else
738
+ login({'login'=>user, 'password'=>password, 'remember'=>true})
739
+ yield
740
+ logout
741
+ end
742
+ end # def session
743
+
744
+ =begin rdoc
745
+
746
+ ==== Bugzilla::User#login(params)
747
+
748
+ Raw Bugzilla API to log into Bugzilla.
749
+
750
+ =end
751
+
752
+ =begin rdoc
753
+
754
+ ==== Bugzilla::User#logout
755
+
756
+ Raw Bugzilla API to log out the user.
757
+
758
+ =end
759
+
760
+ protected
761
+
762
+ def _login(cmd, *args)
763
+ raise ArgumentError, "Invalid parameters" unless args[0].kind_of?(Hash)
764
+
765
+ @iface.call(cmd,args[0])
766
+ end # def _login
767
+
768
+ def _logout(cmd, *args)
769
+ @iface.call(cmd)
770
+ end # def _logout
771
+
772
+ def __offer_account_by_email(cmd, *args)
773
+ # FIXME
774
+ end # def _offer_account_by_email
775
+
776
+ def __create(cmd, *args)
777
+ # FIXME
778
+ end # def _create
779
+
780
+ def __get(cmd, *args)
781
+ requires_version(cmd, 3.4)
782
+ # FIXME
783
+ end # def _get
784
+
785
+ end # class User
786
+
787
+ end # module Bugzilla