Adding some files of recent work.
[clearscm.git] / JIRA / lib / BugzillaUtils.pm
1 =pod
2
3 =head1 NAME $RCSfile: BugzillaUtils.pm,v $
4
5 Some shared functions dealing with Bugzilla. Note this uses DBI to directly
6 access Bugzilla's database. This requires that your userid was granted access.
7 For this I setup adefaria with pretty much read only access.
8
9 =head1 VERSION
10
11 =over
12
13 =item Author
14
15 Andrew DeFaria <Andrew@ClearSCM.com>
16
17 =item Revision
18
19 $Revision: 1.0 $
20
21 =item Created
22
23 Fri Mar 12 10:17:44 PST 2004
24
25 =item Modified
26
27 $Date: 2013/05/30 15:48:06 $
28
29 =head1 ROUTINES
30
31 The following routines are exported:
32
33 =cut
34
35 package BugzillaUtils;
36
37 use strict;
38 use warnings;
39
40 use base 'Exporter';
41
42 use FindBin;
43 use Display;
44 use Carp;
45 use DBI;
46
47 use lib 'lib';
48
49 use JIRAUtils;
50
51 our $bugzilla;
52
53 our @EXPORT = qw (
54   openBugzilla
55   getRelationships
56   getDependencies
57   getBlockers
58   getDuplicates
59   getRelated
60   getBug
61   getBugComments
62   getWatchers
63 );
64
65 sub _checkDBError ($$) {
66   my ($msg, $statement) = @_;
67
68   my $dberr    = $bugzilla->err;
69   my $dberrmsg = $bugzilla->errstr;
70
71   $dberr    ||= 0;
72   $dberrmsg ||= 'Success';
73
74   my $message = '';
75
76   if ($dberr) {
77     my $function = (caller (1)) [3];
78
79     $message = "$function: $msg\nError #$dberr: $dberrmsg\n"
80              . "SQL Statement: $statement";
81   } # if
82
83   $main::log->err ($message, $dberr) if $dberr;
84
85   return;
86 } # _checkDBError
87
88 sub openBugzilla (;$$$$) {
89   my ($dbhost, $dbname, $dbuser, $dbpass) = @_;
90
91   $dbhost //= 'jira-dev';
92   $dbname //= 'bugzilla';
93   $dbuser //= 'adefaria';
94   $dbpass //= 'reader';
95   
96   $main::log->msg ("Connecting to Bugzilla ($dbuser\@$dbhost)");
97   
98   $bugzilla = DBI->connect (
99     "DBI:mysql:$dbname:$dbhost",
100     $dbuser,
101     $dbpass, {
102       PrintError => 0,
103       RaiseError => 1,
104     },
105   );
106   
107   _checkDBError 'Unable to execute statement', 'Connect';
108
109   return $bugzilla;
110 } # openBugzilla
111
112 sub getBug ($;@) {
113   my ($bugid, @fields) = @_;
114   
115   push @fields, 'short_desc' unless @fields;
116   
117   my $statement = 'select ' . join (',', @fields) .
118                   " from bugs where bug_id = $bugid";
119                   
120   my $sth = $bugzilla->prepare ($statement);
121
122   _checkDBError 'Unable to prepare statement', $statement;
123   
124   _checkDBError 'Unable to execute statement', $statement;
125
126   $sth->execute;
127
128   return $sth->fetchrow_hashref;
129 } # getBug
130
131 sub getBugComments ($) {
132   my ($bugid) = @_;
133   
134   my $statement = <<"END";
135 select 
136   bug_id, 
137   bug_when, 
138   substring_index(login_name,'\@',1) as username,
139   thetext
140 from
141   longdescs,
142   profiles
143 where
144   who    = userid and
145   bug_id = $bugid 
146 END
147   
148   my $sth = $bugzilla->prepare ($statement);
149   
150   _checkDBError 'Unable to prepare statement', $statement;
151
152   $sth->execute;
153   
154   _checkDBError 'Unable to execute statement', $statement;
155   
156   my @comments;
157   
158   while (my $comment = $sth->fetchrow_hashref) {
159     my $commentText = <<"END";
160 The following comment was entered by [~$comment->{username}] on $comment->{bug_when}:
161
162 $comment->{thetext}
163 END
164
165     push @comments, $commentText;
166   } # while
167   
168   return \@comments;
169 } # getBugComments
170
171 sub getRelationships ($$$$@) {
172   my ($table, $returnField, $testField, $relationshipType, @bugs) = @_;
173   
174   $main::log->msg ("Getting $relationshipType");
175   
176   my $statement = "select $returnField from $table where $table.$testField = ?";
177
178   my $sth = $bugzilla->prepare ($statement);
179
180   _checkDBError 'Unable to prepare statement', $statement;
181   
182   my %relationships;
183
184   my %bugmap;
185   
186   map {$bugmap{$_} = 1} @bugs unless %bugmap;
187       
188   for my $bugid (@bugs) {
189     $sth->execute ($bugid);
190     
191     _checkDBError 'Unable to exit statement', $statement;
192     
193     my $result = JIRAUtils::findIssue ($bugid, %bugmap);
194     
195     if ($result =~ /^Unable/) {
196       $main::log->warn ($result);
197       
198       $main::total{'Missing JIRA Issues'}++;
199       
200       undef $result;
201     } elsif ($result =~ /^Future/) {
202       $main::total{'Future JIRA Issues'}++;
203       
204       undef $result;
205     } # if
206     
207     my $jiraIssue = $result;
208     my $key       = $jiraIssue || $bugid;
209
210     my @relationships;
211     my $relations = $sth->fetchall_arrayref;
212     my @relations;
213     
214     map {push @relations, $_->[0]} @$relations;
215     
216     for my $relation (@relations) {
217       $jiraIssue = JIRAUtils::findIssue ($relation);
218       
219       if ($jiraIssue =~ /^Unable/ || $jiraIssue =~ /^Future/) {
220         $main::log->warn ($jiraIssue);
221
222         $main::total{'Missing JIRA Issues'}++ if $jiraIssue =~ /^Unable/;
223         $main::total{'Future JIRA Issues'}++  if $jiraIssue =~ /^Future/;
224         
225         push @relationships, $relation;
226       } else {
227         push @relationships, $jiraIssue;
228       } # if
229     } # for
230       
231     push @{$relationships{$key}}, @relationships if @relationships;
232   } # for
233   
234   $main::total{$relationshipType} = keys %relationships;
235   
236   return \%relationships;
237 } # getRelationships
238
239 sub getDependencies (@) {
240   my (@bugs) = @_;
241
242   return getRelationships (
243     'dependencies', # table 
244     'dependson',    # returned field
245     'blocked',      # test field
246     'Depends on',   # relationship
247     @bugs
248   );
249 } # getDependencies
250
251 sub getBlockers (@) {
252   my (@bugs) = @_;
253   
254   return getRelationships (
255     'dependencies', 
256     'blocked', 
257     'dependson', 
258     'Blocks',
259     @bugs
260   );
261 } # getBlockers
262
263 sub getDuplicates (@) {
264   my (@bugs) = @_;
265   
266   return getRelationships (
267     'duplicates', 
268     'dupe', 
269     'dupe_of', 
270     'Duplicates',
271     @bugs
272   );
273 } # getDuplicates
274
275 sub getRelated (@) {
276   my (@bugs) = @_;
277   
278   return getRelationships (
279     'bug_see_also', 
280     'value', 
281     'bug_id', 
282     'Relates',
283     @bugs
284   );
285 } # getRelated
286
287 sub getWatchers ($) {
288   my ($bugid) = @_;
289   
290   my $statement = <<"END";
291 select 
292   profiles.login_name
293 from 
294   cc,
295   profiles
296 where 
297   cc.who = profiles.userid and
298   bug_id = ?
299 END
300
301   my $sth = $bugzilla->prepare ($statement);
302
303   _checkDBError 'Unable to prepare statement', $statement;
304
305   $sth->execute ($bugid);
306
307   _checkDBError 'Unable to execute statement', $statement;
308
309   my @rows = @{$sth->fetchall_arrayref};
310
311   my %watchers;
312
313   for (@rows) {
314     if ($$_[0] =~ /(.*)\@/) {
315       $watchers{$1} = 1;
316     } # if
317
318     $main::total{'Watchers Processed'}++;  
319   } # for
320
321   return %watchers;
322 } # getWatchers