Add dict and list filters

For lists filter <list_name>=<value> returns only those
artifacts that have value in the list.

For dicts filter <dict_name>=<value> returns only those
artifacts that have dicts with the key equal value.

In both cases filters don't accept any operators.

Change-Id: Iea98893b7cd975fa43f23234a78b518c96fe452c
This commit is contained in:
Mike Fedosin 2016-09-18 19:28:05 +03:00
parent 58bf974671
commit 7b47349d02
3 changed files with 92 additions and 28 deletions

View File

@ -435,13 +435,14 @@ def _do_query_filters(filters):
conds = [models.ArtifactProperty.name == field_name]
if key_name is not None:
conds.extend([models.ArtifactProperty.key_name == key_name])
if op != 'in':
fn = op_mappings[op]
conds.extend([fn(getattr(models.ArtifactProperty,
field_type + '_value'), value)])
else:
conds.extend([getattr(models.ArtifactProperty,
field_type + '_value').in_(value)])
if value is not None:
if op != 'in':
fn = op_mappings[op]
conds.extend([fn(getattr(models.ArtifactProperty,
field_type + '_value'), value)])
else:
conds.extend([getattr(models.ArtifactProperty,
field_type + '_value').in_(value)])
prop_conds.append(conds)

View File

@ -531,7 +531,6 @@ class BaseArtifact(base.VersionedObject):
# (field_name, key_name, op, field_type, value)
new_filters = []
for filter_name, filter_value in filters:
key_name = None
if filter_name in ('tags-any', 'tags'):
if ':' in filter_value:
msg = _("Tags are filtered without operator")
@ -539,28 +538,37 @@ class BaseArtifact(base.VersionedObject):
new_filters.append(
(filter_name, None, None, None, filter_value))
continue
elif '.' in filter_name:
filter_name, key_name = filter_name.split('.', 1)
cls._validate_filter_name(filter_name)
op, val = utils.split_filter_op(filter_value)
cls._validate_filter_ops(filter_name, op)
field_type = cls.fields.get(filter_name).element_type
else:
cls._validate_filter_name(filter_name)
op, val = utils.split_filter_op(filter_value)
cls._validate_filter_ops(filter_name, op)
field_type = cls.fields.get(filter_name)
key_name = None
if '.' in filter_name:
filter_name, key_name = filter_name.split('.', 1)
if not isinstance(cls.fields.get(filter_name),
glare_fields.Dict):
msg = _("Field %s is not Dict") % filter_name
raise exception.BadRequest(msg)
cls._validate_filter_name(filter_name)
field_type = cls.fields.get(filter_name)
if isinstance(field_type, glare_fields.List) or isinstance(
field_type, glare_fields.Dict) and key_name is not None:
field_type = field_type.element_type
try:
if op == 'in':
value = [field_type.coerce(cls(), filter_name, value)
for value in
utils.split_filter_value_for_quotes(val)]
if isinstance(field_type, glare_fields.Dict):
new_filters.append((
filter_name, filter_value, None, None, None))
else:
value = field_type.coerce(cls(), filter_name, val)
new_filters.append(
(filter_name, key_name, op,
cls._get_field_type(field_type), value))
op, val = utils.split_filter_op(filter_value)
cls._validate_filter_ops(filter_name, op)
if op == 'in':
value = [field_type.coerce(cls(), filter_name, value)
for value in
utils.split_filter_value_for_quotes(val)]
else:
value = field_type.coerce(cls(), filter_name, val)
new_filters.append(
(filter_name, key_name, op,
cls._get_field_type(field_type), value))
except ValueError:
msg = _("Invalid filter value: %s") % str(val)
raise exception.BadRequest(msg)

View File

@ -425,6 +425,58 @@ class TestList(TestArtifact):
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[5:], result)
def test_artifact_list_dict_filters(self):
lists_of_str = [
['aaa', 'bbb', 'ccc'],
['aaa', 'bbb'],
['aaa', 'ddd'],
['bbb'],
['ccc']
]
dicts_of_str = [
{'aaa': 'z', 'bbb': 'z', 'ccc': 'z'},
{'aaa': 'z', 'bbb': 'z'},
{'aaa': 'z', 'ddd': 'z'},
{'bbb': 'z'},
{'ccc': 'z'}
]
art_list = [self.create_artifact({'name': 'name%s' % i,
'version': '1.0',
'tags': ['tag%s' % i],
'int1': 1024,
'float1': 123.456,
'str1': 'bugaga',
'bool1': True,
'list_of_str': lists_of_str[i],
'dict_of_str': dicts_of_str[i]})
for i in range(5)]
# test list filters
url = '/sample_artifact?list_of_str=aaa&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[:3], result)
url = '/sample_artifact?list_of_str=ccc&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([art_list[0], art_list[4]], result)
url = '/sample_artifact?list_of_str=eee&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result)
# test dict filters
url = '/sample_artifact?dict_of_str=aaa&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[:3], result)
url = '/sample_artifact?dict_of_str=ccc&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([art_list[0], art_list[4]], result)
url = '/sample_artifact?dict_of_str=eee&sort=name'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result)
def test_list_dict_prop_filters(self):
# Create artifact
art_list = [self.create_artifact({'name': 'name0',
@ -485,11 +537,14 @@ class TestList(TestArtifact):
self.assertEqual([], result)
url = '/sample_artifact?dict_of_str'
self.get(url=url, status=400)
self.assertEqual([], result)
url = '/sample_artifact?dict_of_str.pr3=blabla:val3'
self.get(url=url, status=400)
url = '/sample_artifact?list_of_str.pr3=blabla:val3'
self.get(url=url, status=400)
url = '/sample_artifact?dict_of_str.bla=val1'
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result)