Skip to content

Commit cf846bf

Browse files
committed
Implement transitive props for sort and filter
.. in REST API.
1 parent b9ce917 commit cf846bf

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

roundup/rest.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ def get_collection(self, class_name, input):
685685
ss = '+'
686686
pn = p
687687
# Only include properties where we have search permission
688+
# Note that hasSearchPermission already returns 0 for
689+
# non-existing properties.
688690
if self.db.security.hasSearchPermission(
689691
uid, class_name, pn
690692
):
@@ -694,24 +696,34 @@ def get_collection(self, class_name, input):
694696
# like @apiver
695697
pass
696698
else: # serve the filter purpose
699+
p = key.split('.', 1)[0]
697700
try:
698-
prop = class_obj.getprops()[key]
701+
prop = class_obj.getprops()[p]
699702
except KeyError:
700703
raise UsageError("Field %s is not valid for %s class."%(
701-
key, class_name))
704+
p, class_name))
702705
# We drop properties without search permission silently
703706
# This reflects the current behavior of other roundup
704707
# interfaces
708+
# Note that hasSearchPermission already returns 0 for
709+
# non-existing properties.
705710
if not self.db.security.hasSearchPermission(
706711
uid, class_name, key
707712
):
708713
continue
714+
715+
linkcls = class_obj
716+
for p in key.split('.'):
717+
prop = linkcls.getprops(protected = True)[p]
718+
linkcls = getattr (prop, 'classname', None)
719+
if linkcls:
720+
linkcls = self.db.getclass(linkcls)
721+
709722
if isinstance (prop, (hyperdb.Link, hyperdb.Multilink)):
710723
if key in filter_props:
711724
vals = filter_props[key]
712725
else:
713726
vals = []
714-
linkcls = self.db.getclass (prop.classname)
715727
for p in value.split(","):
716728
if prop.try_id_parsing and p.isdigit():
717729
vals.append(p)

test/rest_common.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,44 @@ def testGet(self):
203203
self.assertEqual(self.dummy_client.response_code, 200)
204204
self.assertEqual(results['data']['data'], 'joe')
205205

206+
def testGetTransitive(self):
207+
"""
208+
Retrieve all issues with an 'o' in status
209+
sort by status.name (not order)
210+
"""
211+
base_path = self.db.config['TRACKER_WEB'] + 'rest/data/'
212+
#self.maxDiff=None
213+
self.create_sampledata()
214+
self.db.issue.set('2', status=self.db.status.lookup('closed'))
215+
self.db.issue.set('3', status=self.db.status.lookup('chatting'))
216+
expected={'data':
217+
{'@total_size': 2,
218+
'collection': [
219+
{ 'id': '2',
220+
'link': base_path + 'issue/2',
221+
'status':
222+
{ 'id': '10',
223+
'link': base_path + 'status/10'
224+
}
225+
},
226+
{ 'id': '1',
227+
'link': base_path + 'issue/1',
228+
'status':
229+
{ 'id': '9',
230+
'link': base_path + 'status/9'
231+
}
232+
},
233+
]}
234+
}
235+
form = cgi.FieldStorage()
236+
form.list = [
237+
cgi.MiniFieldStorage('status.name', 'o'),
238+
cgi.MiniFieldStorage('@fields', 'status'),
239+
cgi.MiniFieldStorage('@sort', 'status.name'),
240+
]
241+
results = self.server.get_collection('issue', form)
242+
self.assertDictEqual(expected, results)
243+
206244
def testOutputFormat(self):
207245
""" test of @fields and @verbose implementation """
208246

0 commit comments

Comments
 (0)