Create a list of default filter ops for each field type

Change-Id: I76b2e80c97670b4ae49ba7f8b293519369c4ca9a
This commit is contained in:
Mike Fedosin 2017-04-12 21:26:57 +03:00
parent a1ccdd5bac
commit acf175d335
5 changed files with 82 additions and 97 deletions

View File

@ -87,20 +87,14 @@ class BaseArtifact(base.VersionedObject):
nullable=False, sortable=True,
description="Artifact status."),
'created_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
nullable=False, sortable=True,
description="Datetime when artifact has "
"been created."),
'updated_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
nullable=False, sortable=True,
description="Datetime when artifact has "
"been updated last time."),
'activated_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
required_on_activate=False, sortable=True,
description="Datetime when artifact has became "
"active."),
@ -121,20 +115,15 @@ class BaseArtifact(base.VersionedObject):
description="List of tags added to Artifact."),
'metadata': DictField(fields.String, required_on_activate=False,
element_validators=[validators.MinStrLen(1)],
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN,
attribute.FILTER_NEQ),
description="Key-value dict with useful "
"information about an artifact."),
'visibility': Field(fields.StringField, default='private',
nullable=False, filter_ops=(attribute.FILTER_EQ,),
sortable=True,
nullable=False, sortable=True,
description="Artifact visibility that defines "
"if artifact can be available to "
"other users."),
'version': Field(glare_fields.VersionField, required_on_activate=False,
default=DEFAULT_ARTIFACT_VERSION,
filter_ops=attribute.FILTERS, nullable=False,
default=DEFAULT_ARTIFACT_VERSION, nullable=False,
sortable=True, validators=[validators.Version()],
description="Artifact version(semver).")
}
@ -769,8 +758,7 @@ class BaseArtifact(base.VersionedObject):
:param field_name: blob or blob dict field name
:return: maximum blob size in bytes
"""
return getattr(cls.fields[field_name], 'max_blob_size',
attribute.BlobAttribute.DEFAULT_MAX_BLOB_SIZE)
return getattr(cls.fields[field_name], 'max_blob_size')
@classmethod
def validate_upload_allowed(cls, af, field_name, blob_key=None):

View File

@ -24,6 +24,8 @@ FILTERS = (
FILTER_EQ, FILTER_NEQ, FILTER_IN, FILTER_GT, FILTER_GTE, FILTER_LT,
FILTER_LTE) = ('eq', 'neq', 'in', 'gt', 'gte', 'lt', 'lte')
DEFAULT_MAX_BLOB_SIZE = 10485760
class Attribute(object):
def __init__(self, field_class, mutable=False, required_on_activate=True,
@ -56,19 +58,41 @@ class Attribute(object):
self.system = system
self.sortable = sortable
if field_class is glare_fields.BlobField:
if filter_ops:
raise exc.IncorrectArtifactType(
"Cannot specify filters for blobs")
self.filter_ops = []
try:
default_ops = self.get_allowed_filter_ops(self.element_type)
except AttributeError:
default_ops = self.get_allowed_filter_ops(field_class)
if filter_ops is None:
self.filter_ops = default_ops
else:
self.filter_ops = [FILTER_EQ, FILTER_NEQ, FILTER_IN] \
if filter_ops is None else filter_ops
for op in filter_ops:
if op not in default_ops:
raise exc.IncorrectArtifactType(
"Incorrect filter operator '%s'. "
"Only %s are allowed" % (op, ', '.join(default_ops)))
self.filter_ops = filter_ops
self.field_attrs = ['mutable', 'required_on_activate', 'system',
'sortable', 'filter_ops', 'description']
self.description = description
@staticmethod
def get_allowed_filter_ops(field):
if field in (fields.StringField, fields.String,
glare_fields.ArtifactStatusField):
return [FILTER_EQ, FILTER_NEQ, FILTER_IN]
elif field in (fields.IntegerField, fields.Integer, fields.FloatField,
fields.Float, glare_fields.VersionField):
return FILTERS
elif field in (fields.FlexibleBooleanField, fields.FlexibleBoolean,
glare_fields.Link, glare_fields.LinkFieldType):
return [FILTER_EQ, FILTER_NEQ]
elif field in (glare_fields.BlobField, glare_fields.BlobFieldType):
return []
elif field is fields.DateTimeField:
return [FILTER_LT, FILTER_GT]
def get_default_validators(self):
default = []
if issubclass(self.field_class, fields.StringField):
@ -125,19 +149,20 @@ class Attribute(object):
class CompoundAttribute(Attribute):
def __init__(self, field_class, element_type, element_validators=None,
**kwargs):
super(CompoundAttribute, self).__init__(field_class, **kwargs)
if self.sortable:
raise exc.IncorrectArtifactType("'sortable' must be False for "
"compound type.")
if element_type is None:
raise exc.IncorrectArtifactType("'element_type' must be set for "
"compound type.")
self.element_type = element_type
super(CompoundAttribute, self).__init__(field_class, **kwargs)
self.vo_attrs.append('element_type')
self.field_attrs.append('element_type')
self.element_validators = element_validators or []
if self.sortable:
raise exc.IncorrectArtifactType("'sortable' must be False for "
"compound type.")
def get_element_validators(self):
default_vals = []
@ -178,8 +203,6 @@ class DictAttribute(CompoundAttribute):
super(DictAttribute, self).__init__(glare_fields.Dict, element_type,
**kwargs)
self.validators.append(val_lib.MaxDictSize(max_size))
if element_type is glare_fields.BlobFieldType:
self.filter_ops = []
def get_default_validators(self):
default_vals = []
@ -192,8 +215,6 @@ class DictAttribute(CompoundAttribute):
class BlobAttribute(Attribute):
DEFAULT_MAX_BLOB_SIZE = 10485760
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
super(BlobAttribute, self).__init__(
field_class=glare_fields.BlobField, **kwargs)
@ -202,8 +223,7 @@ class BlobAttribute(Attribute):
class BlobDictAttribute(DictAttribute):
def __init__(self, max_blob_size=BlobAttribute.DEFAULT_MAX_BLOB_SIZE,
**kwargs):
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
super(BlobDictAttribute, self).__init__(
element_type=glare_fields.BlobFieldType, **kwargs)
self.max_blob_size = int(max_blob_size)

View File

@ -210,18 +210,21 @@ class TestList(base.TestArtifact):
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[5:], result)
# visibility=neq:private
url = '/sample_artifact?visibility=neq:private'
self.get(url=url, status=400)
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[5:], result)
url = '/sample_artifact?visibility=neq:public'
self.get(url=url, status=400)
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[:5], result)
url = '/sample_artifact?visibility=blabla'
self.get(url=url, status=200)
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result)
url = '/sample_artifact?visibility=neq:blabla'
self.get(url=url, status=400)
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list, result)
url = '/sample_artifact?name=eq:name0&name=name1&tags=tag1'
result = self.get(url=url)['sample_artifact']
@ -367,7 +370,7 @@ class TestList(base.TestArtifact):
result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result)
for op in ['gt', 'gte', 'lt', 'lte', 'neq']:
for op in ['gt', 'gte', 'lt', 'lte']:
url = '/sample_artifact?dict_of_str.pr3=%s:val3' % op
self.get(url=url, status=400)

View File

@ -21,8 +21,7 @@ from glare.tests.functional import base
fixture_base_props = {
u'activated_at': {
u'description': u'Datetime when artifact has became active.',
u'filter_ops': [u'gt',
u'lt'],
u'filter_ops': [u'lt', u'gt'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
@ -32,8 +31,7 @@ fixture_base_props = {
u'null']},
u'created_at': {
u'description': u'Datetime when artifact has been created.',
u'filter_ops': [u'gt',
u'lt'],
u'filter_ops': [u'lt', u'gt'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
@ -63,9 +61,7 @@ fixture_base_props = {
u'default': {},
u'description': u'Key-value dict with useful information '
u'about an artifact.',
u'filter_ops': [u'eq',
u'in',
u'neq'],
u'filter_ops': [u'eq', u'neq', u'in'],
u'glareType': u'StringDict',
u'maxProperties': 255,
u'required_on_activate': False,
@ -114,8 +110,7 @@ fixture_base_props = {
u'unique': True},
u'updated_at': {
u'description': u'Datetime when artifact has been updated last time.',
u'filter_ops': [u'gt',
u'lt'],
u'filter_ops': [u'lt', u'gt'],
u'format': u'date-time',
u'glareType': u'DateTime',
u'readOnly': True,
@ -141,7 +136,7 @@ fixture_base_props = {
u'description': u'Artifact visibility that defines if '
u'artifact can be available to other '
u'users.',
u'filter_ops': [u'eq'],
u'filter_ops': [u'eq', u'neq', u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'sortable': True,
@ -201,15 +196,13 @@ fixtures = {
u'type': [u'string',
u'null']},
u'link1': {u'filter_ops': [u'eq',
u'neq',
u'in'],
u'neq'],
u'glareType': u'Link',
u'required_on_activate': False,
u'type': [u'string',
u'null']},
u'link2': {u'filter_ops': [u'eq',
u'neq',
u'in'],
u'neq'],
u'glareType': u'Link',
u'required_on_activate': False,
u'type': [u'string',
@ -423,11 +416,7 @@ fixtures = {
u'null']},
u'str1': {u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'required_on_activate': False,
@ -436,11 +425,7 @@ fixtures = {
u'null']},
u'string_mutable': {u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'mutable': True,
@ -450,11 +435,7 @@ fixtures = {
u'string_required': {
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'in'],
u'glareType': u'String',
u'maxLength': 255,
u'type': [u'string',
@ -466,11 +447,7 @@ fixtures = {
None],
u'filter_ops': [u'eq',
u'neq',
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'in'],
u'glareType': u'String',
u'maxLength': 10,
u'required_on_activate': False,
@ -566,8 +543,7 @@ fixtures = {
u'description': u'List of package dependencies for '
u'this package.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'neq'],
u'glareType': u'LinkList',
u'items': {u'type': u'string'},
u'maxItems': 255,
@ -751,7 +727,11 @@ fixtures = {
u'description': u'Minimal disk space required to boot image.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'glareType': u'Integer',
u'minimum': 0,
u'required_on_activate': False,
@ -760,7 +740,11 @@ fixtures = {
u'description': u'Minimal RAM required to boot image.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'in',
u'gt',
u'gte',
u'lt',
u'lte'],
u'glareType': u'Integer',
u'minimum': 0,
u'required_on_activate': False,
@ -828,8 +812,7 @@ fixtures = {
u'that can be used with current '
u'template.',
u'filter_ops': [u'eq',
u'neq',
u'in'],
u'neq'],
u'glareType': u'LinkDict',
u'maxProperties': 255,
u'mutable': True,

View File

@ -32,10 +32,10 @@ class SampleArtifact(base_artifact.BaseArtifact):
VERSION = '1.0'
fields = {
'blob': Blob(required_on_activate=False, mutable=True, filter_ops=[],
'blob': Blob(required_on_activate=False, mutable=True,
description="I am Blob"),
'small_blob': Blob(max_blob_size=10, required_on_activate=False,
mutable=True, filter_ops=[]),
mutable=True),
'link1': Field(glare_fields.Link,
required_on_activate=False),
'link2': Field(glare_fields.Link,
@ -50,24 +50,19 @@ class SampleArtifact(base_artifact.BaseArtifact):
default=False),
'int1': Field(fields.IntegerField,
required_on_activate=False,
sortable=True,
filter_ops=attribute.FILTERS),
sortable=True),
'int2': Field(fields.IntegerField,
sortable=True,
required_on_activate=False,
filter_ops=attribute.FILTERS),
required_on_activate=False),
'float1': Field(fields.FloatField,
sortable=True,
required_on_activate=False,
filter_ops=attribute.FILTERS),
required_on_activate=False),
'float2': Field(fields.FloatField,
sortable=True,
required_on_activate=False,
filter_ops=attribute.FILTERS),
required_on_activate=False),
'str1': Field(fields.StringField,
sortable=True,
required_on_activate=False,
filter_ops=attribute.FILTERS),
required_on_activate=False),
'list_of_str': List(fields.String,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,
@ -97,14 +92,11 @@ class SampleArtifact(base_artifact.BaseArtifact):
validators.MaxDictKeyLen(1000)]),
'string_mutable': Field(fields.StringField,
required_on_activate=False,
mutable=True,
filter_ops=attribute.FILTERS),
mutable=True),
'string_required': Field(fields.StringField,
required_on_activate=True,
filter_ops=attribute.FILTERS),
required_on_activate=True),
'string_validators': Field(fields.StringField,
required_on_activate=False,
filter_ops=attribute.FILTERS,
validators=[
validators.AllowedValues(
['aa', 'bb', 'c' * 11]),
@ -112,7 +104,6 @@ class SampleArtifact(base_artifact.BaseArtifact):
]),
'int_validators': Field(fields.IntegerField,
required_on_activate=False,
filter_ops=attribute.FILTERS,
validators=[
validators.MinNumberSize(10),
validators.MaxNumberSize(20)