From 504db87ae33aae1703931a6226058db49d5b99ea Mon Sep 17 00:00:00 2001 From: Will Woods Date: Fri, 15 Aug 2008 10:27:40 -0400 Subject: Add _update_bugs, implement most of the backend calls for BZ32, add stubs for the rest. --- bugzilla/bugzilla3.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 4 deletions(-) diff --git a/bugzilla/bugzilla3.py b/bugzilla/bugzilla3.py index fae316f..b3c508f 100644 --- a/bugzilla/bugzilla3.py +++ b/bugzilla/bugzilla3.py @@ -40,8 +40,8 @@ class Bugzilla3(bugzilla.base.BugzillaBase): # Connect the backend methods to the XMLRPC methods def _getbugfields(self): '''Get a list of valid fields for bugs.''' - #I don't think BZ3 provides a getbugfields() method, so right - #we fake it by looking at bug #1. Yuck. + # XXX BZ3 doesn't currently provide anything like the getbugfields() + # method, so we fake it by looking at bug #1. Yuck. keylist = self._getbug(1).keys() if 'assigned_to' not in keylist: keylist.append('assigned_to') @@ -213,7 +213,7 @@ class Bugzilla3(bugzilla.base.BugzillaBase): r = self._proxy.Bug.create(data) return r['id'] -# Bugzilla 3.2 adds a whole bunch of new goodies. +# Bugzilla 3.2 adds a whole bunch of new goodies on top of Bugzilla3. class Bugzilla32(Bugzilla3): '''Concrete implementation of the Bugzilla protocol. This one uses the methods provided by standard Bugzilla 3.2.x releases.''' @@ -221,4 +221,132 @@ class Bugzilla32(Bugzilla3): version = '0.1' user_agent = bugzilla.base.user_agent + ' Bugzilla32/%s' % version - # TODO: add goodies (_query, etc) + def _query(self,query): + '''Query bugzilla and return a list of matching bugs. + query must be a dict with fields like those in in querydata['fields']. + You can also pass in keys called 'quicksearch' or 'savedsearch' - + 'quicksearch' will do a quick keyword search like the simple search + on the Bugzilla home page. + 'savedsearch' should be the name of a previously-saved search to + execute. You need to be logged in for this to work. + Returns a dict like this: {'bugs':buglist, + 'sql':querystring} + buglist is a list of dicts describing bugs, and 'sql' contains the SQL + generated by executing the search. + ''' + # The following is true for rhbz; not sure if it's the case for BZ3.2 + #You can specify which columns/keys will be listed in the bugs by + #setting 'column_list' in the query; otherwise the default columns are + #used (see the list in querydefaults['default_column_list']). + return self._proxy.Bug.search(query) + + def _update_bug(self,id,updates): + '''Update a single bug, specified by integer ID or (string) bug alias. + Really just a convenience method for _update_bugs(ids=[id],updates)''' + return self._update_bugs(ids=[id],updates=updates) + + def _update_bugs(self,ids,updates): + '''Update the given fields with the given data in one or more bugs. + ids should be a list of integers or strings, representing bug ids or + aliases. + updates is a dict containing pairs like so: {'fieldname':'newvalue'} + ''' + # TODO document changeable fields & return values + # TODO I think we need to catch XMLRPC exceptions to get + return self._proxy.Bug.update({'ids':ids,'updates':updates}) + + def _addcomment(self,id,comment,private=False, + timestamp='',worktime='',bz_gid=''): + '''Add a comment to the bug with the given ID. Other optional + arguments are as follows: + private: if True, mark this comment as private. + timestamp: comment timestamp, in the form "YYYY-MM-DD HH:MM:SS" + Ignored by BZ32. + worktime: amount of time spent on this comment, in hours + bz_gid: if present, and the entire bug is *not* already private + to this group ID, this comment will be marked private. + ''' + return self._proxy.Bug.add_comment({'id':id, + 'comment':comment, + 'private':private, + 'worktime':worktime}) + + #---- Methods for updating bugs. + + # Eventually - when RHBugzilla is well and truly obsolete - we'll delete + # all of these methods and refactor the Base Bugzilla object so all the bug + # modification calls go through _update_bug. + # Until then, all of these methods are basically just wrappers around it. + + def _setstatus(self,id,status,comment='',private=False,private_in_it=False,nomail=False): + '''Set the status of the bug with the given ID.''' + update={'bug_status':status} + if comment: + update['comment'] = comment + return self._update_bug(ids=[id],updates=update) + + def _closebug(self,id,resolution,dupeid,fixedin,comment,isprivate,private_in_it,nomail): + '''Close the given bug. This is the raw call, and no data checking is + done here. That's up to the closebug method. + Note that the private_in_it and nomail args are ignored.''' + update={'bug_status':'CLOSED','resolution':resolution} + if dupeid: + update['resolution'] = 'DUPLICATE' + update['dupe_id'] = dupeid + if fixedin: + update['fixed_in'] = fixedin + if comment: + update['comment'] = comment + if isprivate: + update['commentprivacy'] = True + return self._update_bug(ids=[id],updates=update) + + def _setassignee(self,id,**data): + '''Raw xmlrpc call to set one of the assignee fields on a bug. + changeAssignment($id, $data, $username, $password) + data: 'assigned_to','reporter','qa_contact','comment' + returns: [$id, $mailresults]''' + # drop empty items + update = dict([(k,v) for k,v in data.iteritems() if v != '']) + return self._update_bug(ids=[id],updates=data) + + def _updatedeps(self,id,deplist): + '''IMPLEMENT ME: update the deps (blocked/dependson) for the given bug. + updateDepends($bug_id,$data,$username,$password,$nodependencyemail) + #data: 'blocked'=>id,'dependson'=>id,'action' => ('add','remove')''' + raise NotImplementedError, "wwoods needs to port this method." + def _updatecc(self,id,cclist,action,comment='',nomail=False): + '''Updates the CC list using the action and account list specified. + cclist must be a list (not a tuple!) of addresses. + action may be 'add', 'remove', or 'makeexact'. + comment specifies an optional comment to add to the bug. + if mail is True, email will be generated for this change. + ''' + raise NotImplementedError, "wwoods needs to port this method." + def _updatewhiteboard(self,id,text,which,action): + '''Update the whiteboard given by 'which' for the given bug. + performs the given action (which may be 'append',' prepend', or + 'overwrite') using the given text.''' + data = {'type':which,'text':text,'action':action} + raise NotImplementedError, "wwoods needs to port this method." + # TODO: update this when the XMLRPC interface grows requestee support + def _updateflags(self,id,flags): + '''Updates the flags associated with a bug report. + data should be a hash of {'flagname':'value'} pairs, like so: + {'needinfo':'?','fedora-cvs':'+'} + You may also add a "nomail":1 item, which will suppress email if set. + + NOTE: the Red Hat XMLRPC interface does not yet support setting the + requestee (as in: needinfo from smartguy@answers.com). Alas.''' + raise NotImplementedError, "wwoods needs to port this method." + + #---- Methods for working with attachments + + # If your bugzilla wants attachments in something other than base64, you + # should override _attachment_encode here. + # If your bugzilla uses non-standard paths for attachment.cgi, you'll + # want to override _attachment_uri here. + + def _attachfile(self,id,**attachdata): + raise NotImplementedError, "wwoods needs to port this method." + -- cgit