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, nullable=False, sortable=True,
description="Artifact status."), description="Artifact status."),
'created_at': Field(fields.DateTimeField, system=True, 'created_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
nullable=False, sortable=True, nullable=False, sortable=True,
description="Datetime when artifact has " description="Datetime when artifact has "
"been created."), "been created."),
'updated_at': Field(fields.DateTimeField, system=True, 'updated_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
nullable=False, sortable=True, nullable=False, sortable=True,
description="Datetime when artifact has " description="Datetime when artifact has "
"been updated last time."), "been updated last time."),
'activated_at': Field(fields.DateTimeField, system=True, 'activated_at': Field(fields.DateTimeField, system=True,
filter_ops=[attribute.FILTER_GT,
attribute.FILTER_LT],
required_on_activate=False, sortable=True, required_on_activate=False, sortable=True,
description="Datetime when artifact has became " description="Datetime when artifact has became "
"active."), "active."),
@ -121,20 +115,15 @@ class BaseArtifact(base.VersionedObject):
description="List of tags added to Artifact."), description="List of tags added to Artifact."),
'metadata': DictField(fields.String, required_on_activate=False, 'metadata': DictField(fields.String, required_on_activate=False,
element_validators=[validators.MinStrLen(1)], element_validators=[validators.MinStrLen(1)],
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN,
attribute.FILTER_NEQ),
description="Key-value dict with useful " description="Key-value dict with useful "
"information about an artifact."), "information about an artifact."),
'visibility': Field(fields.StringField, default='private', 'visibility': Field(fields.StringField, default='private',
nullable=False, filter_ops=(attribute.FILTER_EQ,), nullable=False, sortable=True,
sortable=True,
description="Artifact visibility that defines " description="Artifact visibility that defines "
"if artifact can be available to " "if artifact can be available to "
"other users."), "other users."),
'version': Field(glare_fields.VersionField, required_on_activate=False, 'version': Field(glare_fields.VersionField, required_on_activate=False,
default=DEFAULT_ARTIFACT_VERSION, default=DEFAULT_ARTIFACT_VERSION, nullable=False,
filter_ops=attribute.FILTERS, nullable=False,
sortable=True, validators=[validators.Version()], sortable=True, validators=[validators.Version()],
description="Artifact version(semver).") description="Artifact version(semver).")
} }
@ -769,8 +758,7 @@ class BaseArtifact(base.VersionedObject):
:param field_name: blob or blob dict field name :param field_name: blob or blob dict field name
:return: maximum blob size in bytes :return: maximum blob size in bytes
""" """
return getattr(cls.fields[field_name], 'max_blob_size', return getattr(cls.fields[field_name], 'max_blob_size')
attribute.BlobAttribute.DEFAULT_MAX_BLOB_SIZE)
@classmethod @classmethod
def validate_upload_allowed(cls, af, field_name, blob_key=None): 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_EQ, FILTER_NEQ, FILTER_IN, FILTER_GT, FILTER_GTE, FILTER_LT,
FILTER_LTE) = ('eq', 'neq', 'in', 'gt', 'gte', 'lt', 'lte') FILTER_LTE) = ('eq', 'neq', 'in', 'gt', 'gte', 'lt', 'lte')
DEFAULT_MAX_BLOB_SIZE = 10485760
class Attribute(object): class Attribute(object):
def __init__(self, field_class, mutable=False, required_on_activate=True, def __init__(self, field_class, mutable=False, required_on_activate=True,
@ -56,19 +58,41 @@ class Attribute(object):
self.system = system self.system = system
self.sortable = sortable self.sortable = sortable
if field_class is glare_fields.BlobField: try:
if filter_ops: default_ops = self.get_allowed_filter_ops(self.element_type)
raise exc.IncorrectArtifactType( except AttributeError:
"Cannot specify filters for blobs") default_ops = self.get_allowed_filter_ops(field_class)
self.filter_ops = []
if filter_ops is None:
self.filter_ops = default_ops
else: else:
self.filter_ops = [FILTER_EQ, FILTER_NEQ, FILTER_IN] \ for op in filter_ops:
if filter_ops is None else 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', self.field_attrs = ['mutable', 'required_on_activate', 'system',
'sortable', 'filter_ops', 'description'] 'sortable', 'filter_ops', 'description']
self.description = 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): def get_default_validators(self):
default = [] default = []
if issubclass(self.field_class, fields.StringField): if issubclass(self.field_class, fields.StringField):
@ -125,19 +149,20 @@ class Attribute(object):
class CompoundAttribute(Attribute): class CompoundAttribute(Attribute):
def __init__(self, field_class, element_type, element_validators=None, def __init__(self, field_class, element_type, element_validators=None,
**kwargs): **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: if element_type is None:
raise exc.IncorrectArtifactType("'element_type' must be set for " raise exc.IncorrectArtifactType("'element_type' must be set for "
"compound type.") "compound type.")
self.element_type = element_type self.element_type = element_type
super(CompoundAttribute, self).__init__(field_class, **kwargs)
self.vo_attrs.append('element_type') self.vo_attrs.append('element_type')
self.field_attrs.append('element_type') self.field_attrs.append('element_type')
self.element_validators = element_validators or [] self.element_validators = element_validators or []
if self.sortable:
raise exc.IncorrectArtifactType("'sortable' must be False for "
"compound type.")
def get_element_validators(self): def get_element_validators(self):
default_vals = [] default_vals = []
@ -178,8 +203,6 @@ class DictAttribute(CompoundAttribute):
super(DictAttribute, self).__init__(glare_fields.Dict, element_type, super(DictAttribute, self).__init__(glare_fields.Dict, element_type,
**kwargs) **kwargs)
self.validators.append(val_lib.MaxDictSize(max_size)) self.validators.append(val_lib.MaxDictSize(max_size))
if element_type is glare_fields.BlobFieldType:
self.filter_ops = []
def get_default_validators(self): def get_default_validators(self):
default_vals = [] default_vals = []
@ -192,8 +215,6 @@ class DictAttribute(CompoundAttribute):
class BlobAttribute(Attribute): class BlobAttribute(Attribute):
DEFAULT_MAX_BLOB_SIZE = 10485760
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs): def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
super(BlobAttribute, self).__init__( super(BlobAttribute, self).__init__(
field_class=glare_fields.BlobField, **kwargs) field_class=glare_fields.BlobField, **kwargs)
@ -202,8 +223,7 @@ class BlobAttribute(Attribute):
class BlobDictAttribute(DictAttribute): class BlobDictAttribute(DictAttribute):
def __init__(self, max_blob_size=BlobAttribute.DEFAULT_MAX_BLOB_SIZE, def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
**kwargs):
super(BlobDictAttribute, self).__init__( super(BlobDictAttribute, self).__init__(
element_type=glare_fields.BlobFieldType, **kwargs) element_type=glare_fields.BlobFieldType, **kwargs)
self.max_blob_size = int(max_blob_size) 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']) result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual(art_list[5:], result) self.assertEqual(art_list[5:], result)
# visibility=neq:private
url = '/sample_artifact?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' 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' 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' 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' url = '/sample_artifact?name=eq:name0&name=name1&tags=tag1'
result = self.get(url=url)['sample_artifact'] result = self.get(url=url)['sample_artifact']
@ -367,7 +370,7 @@ class TestList(base.TestArtifact):
result = sort_results(self.get(url=url)['sample_artifact']) result = sort_results(self.get(url=url)['sample_artifact'])
self.assertEqual([], result) 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 url = '/sample_artifact?dict_of_str.pr3=%s:val3' % op
self.get(url=url, status=400) self.get(url=url, status=400)

View File

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

View File

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