Skip to content

Commit 5d2f7a8

Browse files
committed
rhbugzilla: Fix include_fields=["fixed_in"] (bz 1084887)
We want bug.fixed_in to map to bug.cf_fixed_in, but we _don't_ want to do that mapping for include_fields, since cf_fixed_in is the actual API name. Differentiate field_aliases by whether they are bug attribute or API aliases.
1 parent cb4b009 commit 5d2f7a8

6 files changed

Lines changed: 100 additions & 57 deletions

File tree

bugzilla/base.py

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,28 @@ class BugzillaError(Exception):
196196
pass
197197

198198

199+
class _FieldAlias(object):
200+
"""
201+
Track API attribute names that differ from what we expose in users.
202+
203+
For example, originally 'short_desc' was the name of the property that
204+
maps to 'summary' on modern bugzilla. We want pre-existing API users
205+
to be able to continue to use Bug.short_desc, and
206+
query({"short_desc": "foo"}). This class tracks that mapping.
207+
208+
@oldname: The old attribute name
209+
@newname: The modern attribute name
210+
@is_api: If True, use this mapping for values sent to the xmlrpc API
211+
(like the query example)
212+
@is_bug: If True, use this mapping for Bug attribute names.
213+
"""
214+
def __init__(self, newname, oldname, is_api=True, is_bug=True):
215+
self.newname = newname
216+
self.oldname = oldname
217+
self.is_api = is_api
218+
self.is_bug = is_bug
219+
220+
199221
class BugzillaBase(object):
200222
'''An object which represents the data and methods exported by a Bugzilla
201223
instance. Uses xmlrpclib to do its thing. You'll want to create one thusly:
@@ -309,6 +331,29 @@ def __init__(self, url=None, user=None, password=None, cookiefile=-1,
309331
cookiefile = os.path.expanduser('~/.bugzillacookies')
310332
self.cookiefile = cookiefile
311333

334+
# List of field aliases. Maps old style RHBZ parameter
335+
# names to actual upstream values. Used for createbug() and
336+
# query include_fields at least.
337+
self._field_aliases = []
338+
self._add_field_alias('summary', 'short_desc')
339+
self._add_field_alias('description', 'comment')
340+
self._add_field_alias('platform', 'rep_platform')
341+
self._add_field_alias('severity', 'bug_severity')
342+
self._add_field_alias('status', 'bug_status')
343+
self._add_field_alias('id', 'bug_id')
344+
self._add_field_alias('blocks', 'blockedby')
345+
self._add_field_alias('blocks', 'blocked')
346+
self._add_field_alias('depends_on', 'dependson')
347+
self._add_field_alias('creator', 'reporter')
348+
self._add_field_alias('url', 'bug_file_loc')
349+
self._add_field_alias('dupe_of', 'dupe_id')
350+
self._add_field_alias('dupe_of', 'dup_id')
351+
self._add_field_alias('comments', 'longdescs')
352+
self._add_field_alias('creation_time', 'opendate')
353+
self._add_field_alias('creation_time', 'creation_ts')
354+
self._add_field_alias('whiteboard', 'status_whiteboard')
355+
self._add_field_alias('last_change_time', 'delta_ts')
356+
312357
if url:
313358
self.connect(url)
314359

@@ -362,6 +407,17 @@ def _product_name_to_id(self, product):
362407
return p['id']
363408
raise ValueError('No product named "%s"' % product)
364409

410+
def _add_field_alias(self, *args, **kwargs):
411+
self._field_aliases.append(_FieldAlias(*args, **kwargs))
412+
413+
def _get_bug_aliases(self):
414+
return [(f.newname, f.oldname)
415+
for f in self._field_aliases if f.is_bug]
416+
417+
def _get_api_aliases(self):
418+
return [(f.newname, f.oldname)
419+
for f in self._field_aliases if f.is_api]
420+
365421

366422
###################
367423
# Cookie handling #
@@ -763,31 +819,6 @@ def _find_comps():
763819
# like this, for upstream bz it returns all info for every Bug.get()
764820
getbug_extra_fields = []
765821

766-
# List of field aliases. Maps old style RHBZ parameter names to actual
767-
# upstream values. Used for createbug() and query include_fields at
768-
# least.
769-
#
770-
# Format is (currentname, oldname)
771-
field_aliases = (
772-
('summary', 'short_desc'),
773-
('description', 'comment'),
774-
('platform', 'rep_platform'),
775-
('severity', 'bug_severity'),
776-
('status', 'bug_status'),
777-
('id', 'bug_id'),
778-
('blocks', 'blockedby'),
779-
('blocks', 'blocked'),
780-
('depends_on', 'dependson'),
781-
('creator', 'reporter'),
782-
('url', 'bug_file_loc'),
783-
('dupe_of', 'dupe_id'),
784-
('dupe_of', 'dup_id'),
785-
('comments', 'longdescs'),
786-
('creation_time', 'opendate'),
787-
('creation_time', 'creation_ts'),
788-
('whiteboard', 'status_whiteboard'),
789-
('last_change_time', 'delta_ts'),
790-
)
791822

792823
def _getbugs(self, idlist, simple=False, permissive=True):
793824
'''
@@ -1432,11 +1463,11 @@ def _validate_createbug(self, *args, **kwargs):
14321463

14331464
# If we're getting a call that uses an old fieldname, convert it to the
14341465
# new fieldname instead.
1435-
for newfield, oldfield in self.field_aliases:
1436-
if (newfield in self.createbug_required and
1437-
newfield not in data and
1438-
oldfield in data):
1439-
data[newfield] = data.pop(oldfield)
1466+
for newname, oldname in self._get_api_aliases():
1467+
if (newname in self.createbug_required and
1468+
newname not in data and
1469+
oldname in data):
1470+
data[newname] = data.pop(oldname)
14401471

14411472
# Back compat handling for check_args
14421473
if "check_args" in data:

bugzilla/bug.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def __getattr__(self, name):
7272
return self.__dict__[name]
7373

7474
# Check field aliases
75-
for newname, oldname in self.bugzilla.field_aliases:
75+
for newname, oldname in self.bugzilla._get_bug_aliases():
7676
if name == oldname and newname in self.__dict__:
7777
return self.__dict__[newname]
7878

@@ -123,7 +123,7 @@ def _update_dict(self, newdict):
123123
'''
124124
self.bugzilla.post_translation({}, newdict)
125125

126-
for newname, oldname in self.bugzilla.field_aliases:
126+
for newname, oldname in self.bugzilla._get_bug_aliases():
127127
if not oldname in newdict:
128128
continue
129129

bugzilla/bugzilla4.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,8 @@ def build_query(self, **kwargs):
3333

3434
# 'include_fields' only available for Bugzilla4+
3535
include_fields = kwargs.get('include_fields', None)
36-
if not include_fields is None:
37-
query["include_fields"] = include_fields
38-
39-
# Translate old style fields
40-
for newname, oldname in self.field_aliases:
36+
if include_fields is not None:
37+
for newname, oldname in self._get_api_aliases():
4138
if oldname in include_fields:
4239
include_fields.remove(oldname)
4340
if newname not in include_fields:
@@ -46,6 +43,7 @@ def build_query(self, **kwargs):
4643
# We always need the id
4744
if 'id' not in include_fields:
4845
include_fields.append('id')
46+
query["include_fields"] = include_fields
4947

5048
return query
5149

bugzilla/rhbugzilla.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ def __init__(self, **kwargs):
5151

5252
_parent.__init__(self, **kwargs)
5353

54+
def _add_both_alias(newname, origname):
55+
self._add_field_alias(newname, origname, is_api=False)
56+
self._add_field_alias(origname, newname, is_bug=False)
57+
58+
_add_both_alias('fixed_in', 'cf_fixed_in')
59+
_add_both_alias('qa_whiteboard', 'cf_qa_whiteboard')
60+
_add_both_alias('devel_whiteboard', 'cf_devel_whiteboard')
61+
_add_both_alias('internal_whiteboard', 'cf_internal_whiteboard')
62+
63+
self._add_field_alias('component', 'components', is_bug=False)
64+
self._add_field_alias('version', 'versions', is_bug=False)
65+
self._add_field_alias('sub_component', 'sub_components', is_bug=False)
66+
67+
# flags format isn't exactly the same but it's the closest approx
68+
self._add_field_alias('flags', 'flag_types')
69+
70+
5471
getbug_extra_fields = (
5572
_parent.getbug_extra_fields + [
5673
"attachments", "comments", "description",
@@ -59,17 +76,6 @@ def __init__(self, **kwargs):
5976
]
6077
)
6178

62-
field_aliases = (
63-
_parent.field_aliases + (
64-
('fixed_in', 'cf_fixed_in'),
65-
('qa_whiteboard', 'cf_qa_whiteboard'),
66-
('devel_whiteboard', 'cf_devel_whiteboard'),
67-
('internal_whiteboard', 'cf_internal_whiteboard'),
68-
# Format isn't exactly the same but it's the closest approximation
69-
('flags', 'flag_types'),
70-
)
71-
)
72-
7379

7480
######################
7581
# Bug update methods #
@@ -138,14 +144,8 @@ def pre_translation(self, query):
138144
query['include_fields'] = query['column_list']
139145
del query['column_list']
140146

141-
include_aliases = (
142-
("component", "components"),
143-
("version", "versions"),
144-
("sub_components", "sub_component"),
145-
)
146-
147147
include_fields = query['include_fields']
148-
for newname, oldname in (self.field_aliases + include_aliases):
148+
for newname, oldname in self._get_api_aliases():
149149
if oldname in include_fields:
150150
include_fields.remove(oldname)
151151
if newname not in include_fields:

tests/bug.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
from bugzilla import RHBugzilla
1515
from bugzilla.bug import _Bug
1616

17-
import tests
18-
1917

2018
rhbz = RHBugzilla(cookiefile=None)
2119

tests/query.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Unit tests for building query strings with bin/bugzilla
1010
'''
1111

12+
import copy
1213
import os
1314
import unittest
1415

@@ -242,6 +243,21 @@ class RHBZTest(BZ4Test):
242243
'component': ["lvm2", "kernel"],
243244
'sub_components': ["Command-line tools (RHEL5)"]}
244245

246+
def testTranslation(self):
247+
in_query = {
248+
"fixed_in": "foo.bar",
249+
"product": "some-product",
250+
"cf_devel_whiteboard": "some_devel_whiteboard",
251+
"include_fields": ["fixed_in",
252+
"components", "cf_devel_whiteboard"],
253+
}
254+
out_query = copy.deepcopy(in_query)
255+
self.bz.pre_translation(out_query)
256+
257+
in_query["include_fields"] = [
258+
"cf_devel_whiteboard", "cf_fixed_in", "component"]
259+
self.assertDictEqual(in_query, out_query)
260+
245261

246262
class TestURLToQuery(BZ34Test):
247263
def _check(self, url, query):

0 commit comments

Comments
 (0)