Rename 'attributes' to 'fields'

Currently there is a confusion in naming, because in
different parts of the code there are names: 'fields' and
'attributes', which represent the same things.

To avoid this, new convention is suggested:
1. Entities that define artifact structure are called
'fields'. Example of fields: 'id', 'version', 'tags',
'owner'.

2. There are two types of fields: basic and custom. Basic
are fields from Base artifact type and each artifact
type has them. Custom ones belong to specific artifact type
only.
Example of custom fields: 'disk_format' for images, 'package'
for murano_package.

3. Each field has properties. Example of field properties:
sortable, nullable, required_on_activate.

This patch renames all ocasions of attributes into fields.
For backward compatibility classes and modules can be
alternatevely accessed by their previous names.

Change-Id: Iec521112020f7cb7badd010bb21be130bf5825c0
This commit is contained in:
Mike Fedosin 2017-06-05 18:47:22 +03:00
parent 80ae6e2c90
commit 2199963911
11 changed files with 162 additions and 140 deletions

View File

@ -16,10 +16,10 @@ from oslo_versionedobjects import fields
from glare.common import exception
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Field = wrappers.Field.init
class All(base.BaseArtifact):

View File

@ -30,9 +30,9 @@ from glare.common import utils
from glare.db import artifact_api
from glare.i18n import _
from glare import locking
from glare.objects.meta import attribute
from glare.objects.meta import fields as glare_fields
from glare.objects.meta import validators
from glare.objects.meta import wrappers
artifact_opts = [
cfg.BoolOpt('delayed_delete', default=False,
@ -66,10 +66,10 @@ class BaseArtifact(base.VersionedObject):
STATUS = glare_fields.ArtifactStatusField
Field = attribute.Attribute.init
DictField = attribute.DictAttribute.init
ListField = attribute.ListAttribute.init
Blob = attribute.BlobAttribute.init
Field = wrappers.Field.init
DictField = wrappers.DictField.init
ListField = wrappers.ListField.init
Blob = wrappers.BlobField.init
fields = {
'id': Field(fields.StringField, system=True,
@ -197,13 +197,13 @@ class BaseArtifact(base.VersionedObject):
:return: artifact with initialized values
"""
af = cls(context)
# setup default values for all non specified attributes
default_attrs = []
for attr in af.fields:
if attr not in values:
default_attrs.append(attr)
if default_attrs:
af.obj_set_defaults(*default_attrs)
# setup default values for all non specified fields
default_fields = []
for field in af.fields:
if field not in values:
default_fields.append(field)
if default_fields:
af.obj_set_defaults(*default_fields)
# apply values specified by user
for name, value in six.iteritems(values):
@ -300,8 +300,8 @@ class BaseArtifact(base.VersionedObject):
"""Validate if fields can be updated in artifact."""
af_status = cls.STATUS.DRAFTED if af is None else af.status
if af_status not in (cls.STATUS.ACTIVE, cls.STATUS.DRAFTED):
msg = _("Forbidden to change attributes "
"if artifact not active or drafted.")
msg = _("Forbidden to change fields "
"if artifact is not active or drafted.")
raise exception.Forbidden(message=msg)
for field_name in field_names:
@ -667,7 +667,8 @@ class BaseArtifact(base.VersionedObject):
for name, type_obj in six.iteritems(af.fields):
if type_obj.required_on_activate and getattr(af, name) is None:
msg = _("'%s' attribute must be set before activation") % name
msg = _(
"'%s' field value must be set before activation") % name
raise exception.BadRequest(msg)
cls.validate_activate(context, af)
@ -888,17 +889,17 @@ class BaseArtifact(base.VersionedObject):
return res
@classmethod
def _schema_attr(cls, attr, attr_name=''):
attr_type = utils.get_schema_type(attr)
def _schema_field(cls, field, field_name=''):
field_type = utils.get_schema_type(field)
schema = {}
# generate schema for validators
for val in getattr(attr, 'validators', []):
for val in getattr(field, 'validators', []):
schema.update(val.to_jsonschema())
schema['type'] = (attr_type
if not attr.nullable else [attr_type, 'null'])
schema['glareType'] = utils.get_glare_type(attr)
schema['type'] = (field_type
if not field.nullable else [field_type, 'null'])
schema['glareType'] = utils.get_glare_type(field)
output_blob_schema = {
'type': ['object', 'null'],
'properties': {
@ -917,15 +918,15 @@ class BaseArtifact(base.VersionedObject):
'additionalProperties': False
}
if attr.system:
if field.system:
schema['readOnly'] = True
if isinstance(attr, glare_fields.Dict):
element_type = (utils.get_schema_type(attr.element_type)
if hasattr(attr, 'element_type')
if isinstance(field, glare_fields.Dict):
element_type = (utils.get_schema_type(field.element_type)
if hasattr(field, 'element_type')
else 'string')
if attr.element_type is glare_fields.BlobFieldType:
if field.element_type is glare_fields.BlobFieldType:
schema['additionalProperties'] = output_blob_schema
else:
if schema.get('properties'):
@ -941,34 +942,34 @@ class BaseArtifact(base.VersionedObject):
else:
schema['additionalProperties'] = {'type': element_type}
if attr_type == 'array':
if field_type == 'array':
schema['items'] = {
'type': (utils.get_schema_type(attr.element_type)
if hasattr(attr, 'element_type')
'type': (utils.get_schema_type(field.element_type)
if hasattr(field, 'element_type')
else 'string')}
if isinstance(attr, glare_fields.BlobField):
if isinstance(field, glare_fields.BlobField):
schema.update(output_blob_schema)
if isinstance(attr, fields.DateTimeField):
if isinstance(field, fields.DateTimeField):
schema['format'] = 'date-time'
if attr_name == 'status':
if field_name == 'status':
schema['enum'] = list(
glare_fields.ArtifactStatusField.ARTIFACT_STATUS)
if attr.description:
schema['description'] = attr.description
if attr.mutable:
if field.description:
schema['description'] = field.description
if field.mutable:
schema['mutable'] = True
if attr.sortable:
if field.sortable:
schema['sortable'] = True
if not attr.required_on_activate:
if not field.required_on_activate:
schema['required_on_activate'] = False
if attr._default is not None:
schema['default'] = attr._default
if field._default is not None:
schema['default'] = field._default
schema['filter_ops'] = attr.filter_ops
schema['filter_ops'] = field.filter_ops
return schema
@ -976,9 +977,9 @@ class BaseArtifact(base.VersionedObject):
def gen_schemas(cls):
"""Return json schema representation of the artifact type."""
schemas_prop = {}
for attr_name, attr in six.iteritems(cls.fields):
schemas_prop[attr_name] = cls._schema_attr(
attr, attr_name=attr_name)
for field_name, field in six.iteritems(cls.fields):
schemas_prop[field_name] = cls._schema_field(
field, field_name=field_name)
schemas = {'properties': schemas_prop,
'name': cls.get_type_name(),
'version': cls.VERSION,

View File

@ -15,13 +15,9 @@
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Blob = attribute.BlobAttribute.init
Dict = attribute.DictAttribute.init
BlobDict = attribute.BlobDictAttribute.init
Blob = wrappers.BlobField.init
class HeatEnvironment(base.BaseArtifact):

View File

@ -16,14 +16,13 @@
from oslo_versionedobjects import fields
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import fields as glare_fields
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Blob = attribute.BlobAttribute.init
Dict = attribute.DictAttribute.init
BlobDict = attribute.BlobDictAttribute.init
Field = wrappers.Field.init
Blob = wrappers.BlobField.init
Dict = wrappers.DictField.init
Folder = wrappers.FolderField.init
class HeatTemplate(base.BaseArtifact):
@ -35,10 +34,10 @@ class HeatTemplate(base.BaseArtifact):
"that can be used with current "
"template."),
'template': Blob(description="Heat template body."),
'nested_templates': BlobDict(description="Dict of nested templates "
"where key is the name of "
"template and value is "
"nested template body."),
'nested_templates': Folder(description="Dict of nested templates "
"where key is the name of "
"template and value is "
"nested template body."),
'default_envs': Dict(fields.String, mutable=True,
description="Default environments that can be "
"applied to the template if no "

View File

@ -17,12 +17,11 @@
from oslo_versionedobjects import fields
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import validators
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Blob = attribute.BlobAttribute.init
Field = wrappers.Field.init
Blob = wrappers.BlobField.init
class Image(base.BaseArtifact):

View File

@ -0,0 +1,18 @@
# Copyright 2017 Nokia
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from glare.objects.meta import wrappers
# for backward compatibility when 'wrappers' module was called 'attribute'
attribute = wrappers

View File

@ -43,7 +43,7 @@ class ArtifactStatusField(fields.StateMachine):
class Version(fields.FieldType):
@staticmethod
def coerce(obj, attr, value):
def coerce(obj, field, value):
return str(semantic_version.Version.coerce(str(value)))
@ -77,7 +77,7 @@ class BlobFieldType(fields.FieldType):
}
@staticmethod
def coerce(obj, attr, value):
def coerce(obj, field, value):
"""Validate and store blob info inside oslo.vo"""
if not isinstance(value, dict):
raise ValueError(_("Blob value must be dict. Got %s type instead")
@ -91,7 +91,7 @@ class BlobFieldType(fields.FieldType):
return value
@staticmethod
def to_primitive(obj, attr, value):
def to_primitive(obj, field, value):
prim = {key: val for key, val in six.iteritems(value)
if key != 'id'}
@ -100,7 +100,7 @@ class BlobFieldType(fields.FieldType):
"name": obj.get_type_name(),
'id': obj.id
}
blob_path = attr.split('[')
blob_path = field.split('[')
url = url + blob_path[0]
if len(blob_path) > 1:
url += '/%s' % blob_path[1][1:-2]
@ -133,16 +133,16 @@ class LinkFieldType(fields.FieldType):
"extract type_name from link %s"), link)
@staticmethod
def coerce(obj, attr, value):
def coerce(obj, field, value):
# to remove the existing link user sets its value to None,
# we have to consider this case.
if value is None:
return value
# check that value is string
if not isinstance(value, six.string_types):
raise ValueError(_('A string is required in field %(attr)s, '
raise ValueError(_('A string is required in field %(field)s, '
'not a %(type)s') %
{'attr': attr, 'type': type(value).__name__})
{'field': field, 'type': type(value).__name__})
# determine if link is external or internal
external = LinkFieldType.is_external(value)
# validate link itself
@ -156,10 +156,10 @@ class LinkFieldType(fields.FieldType):
if len(result) != 4 or result[1] != 'artifacts':
raise ValueError(
_('Link %(link)s is not valid in field '
'%(attr)s. The link must be either valid url or '
'%(field)s. The link must be either valid url or '
'reference to artifact. Example: '
'/artifacts/<artifact_type>/<artifact_id>'
) % {'link': value, 'attr': attr})
) % {'link': value, 'field': field})
return value

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
"""This file contains classes that wrap nat"""
import six
from oslo_versionedobjects import fields
@ -27,11 +29,11 @@ FILTERS = (
DEFAULT_MAX_BLOB_SIZE = 10485760
class Attribute(object):
class Field(object):
def __init__(self, field_class, mutable=False, required_on_activate=True,
system=False, validators=None, nullable=True, default=None,
sortable=False, filter_ops=None, description=""):
"""Init and validate attribute."""
"""Init and validate field."""
if not issubclass(field_class, fields.AutoTypedField):
raise exc.IncorrectArtifactType(
"Field class %s must be sub-class of AutoTypedField." %
@ -43,15 +45,15 @@ class Attribute(object):
if isinstance(v, val_lib.MaxStrLen):
if v.size > 255 and sortable:
raise exc.IncorrectArtifactType(
"It's forbidden to make attribute %(attr)s "
"It's forbidden to make field %(field)s "
"sortable if string length can be more than 255 "
"symbols. Maximal allowed length now: %(max)d" %
{"attr": str(field_class), 'max': v.size})
{"field": str(field_class), 'max': v.size})
self.field_class = field_class
self.nullable = nullable
self.default = default
self.vo_attrs = ['nullable', 'default']
self.vo_props = ['nullable', 'default']
self.mutable = mutable
self.required_on_activate = required_on_activate
@ -73,7 +75,7 @@ class Attribute(object):
"Only %s are allowed" % (op, ', '.join(default_ops)))
self.filter_ops = filter_ops
self.field_attrs = ['mutable', 'required_on_activate', 'system',
self.field_props = ['mutable', 'required_on_activate', 'system',
'sortable', 'filter_ops', 'description']
self.description = description
@ -104,13 +106,13 @@ class Attribute(object):
def get_field(self):
# init the field
vo_attrs = {attr_name: getattr(self, attr_name)
for attr_name in self.vo_attrs}
field = self.field_class(**vo_attrs)
# setup custom field attrs
field_attrs = {attr_name: getattr(self, attr_name)
for attr_name in self.field_attrs}
for prop, value in six.iteritems(field_attrs):
vo_props = {prop_name: getattr(self, prop_name)
for prop_name in self.vo_props}
field = self.field_class(**vo_props)
# setup custom field properties
field_props = {prop_name: getattr(self, prop_name)
for prop_name in self.field_props}
for prop, value in six.iteritems(field_props):
setattr(field, prop, value)
# apply custom validators
@ -123,16 +125,16 @@ class Attribute(object):
vals.append(def_val)
def wrapper(coerce_func):
def coerce_wrapper(obj, attr, value):
def coerce_wrapper(obj, field, value):
try:
val = coerce_func(obj, attr, value)
val = coerce_func(obj, field, value)
if val is not None:
for check_func in vals:
check_func(val)
return val
except (KeyError, ValueError, TypeError) as e:
msg = "Type: %s. Field: %s. Exception: %s" % (
obj.get_type_name(), attr, str(e))
obj.get_type_name(), field, str(e))
raise exc.BadRequest(message=msg)
return coerce_wrapper
@ -142,11 +144,11 @@ class Attribute(object):
@classmethod
def init(cls, *args, **kwargs):
"""Fabric to build attributes."""
"""Fabric to build fields."""
return cls(*args, **kwargs).get_field()
class CompoundAttribute(Attribute):
class CompoundField(Field):
def __init__(self, field_class, element_type, element_validators=None,
**kwargs):
if element_type is None:
@ -154,10 +156,10 @@ class CompoundAttribute(Attribute):
"compound type.")
self.element_type = element_type
super(CompoundAttribute, self).__init__(field_class, **kwargs)
super(CompoundField, self).__init__(field_class, **kwargs)
self.vo_attrs.append('element_type')
self.field_attrs.append('element_type')
self.vo_props.append('element_type')
self.field_props.append('element_type')
self.element_validators = element_validators or []
if self.sortable:
@ -177,54 +179,63 @@ class CompoundAttribute(Attribute):
return default_vals + self.element_validators
class ListAttribute(CompoundAttribute):
class ListField(CompoundField):
def __init__(self, element_type, max_size=255, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = []
if element_type is glare_fields.BlobField:
raise exc.IncorrectArtifactType("List of blobs is not allowed "
"to be specified in artifact.")
super(ListAttribute, self).__init__(glare_fields.List, element_type,
**kwargs)
super(ListField, self).__init__(glare_fields.List, element_type,
**kwargs)
self.validators.append(val_lib.MaxListSize(max_size))
def get_default_validators(self):
default_vals = []
elem_val = val_lib.ListElementValidator(
super(ListAttribute, self).get_element_validators())
super(ListField, self).get_element_validators())
default_vals.append(elem_val)
return default_vals
class DictAttribute(CompoundAttribute):
class DictField(CompoundField):
def __init__(self, element_type, max_size=255, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = {}
super(DictAttribute, self).__init__(glare_fields.Dict, element_type,
**kwargs)
super(DictField, self).__init__(glare_fields.Dict, element_type,
**kwargs)
self.validators.append(val_lib.MaxDictSize(max_size))
def get_default_validators(self):
default_vals = []
elem_val = val_lib.DictElementValidator(
super(DictAttribute, self).get_element_validators())
super(DictField, self).get_element_validators())
default_vals.append(elem_val)
default_vals.append(val_lib.MaxDictKeyLen(255))
default_vals.append(val_lib.MinDictKeyLen(1))
return default_vals
class BlobAttribute(Attribute):
class BlobField(Field):
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
super(BlobAttribute, self).__init__(
super(BlobField, self).__init__(
field_class=glare_fields.BlobField, **kwargs)
self.max_blob_size = int(max_blob_size)
self.field_attrs.append('max_blob_size')
self.field_props.append('max_blob_size')
class BlobDictAttribute(DictAttribute):
class FolderField(DictField):
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
super(BlobDictAttribute, self).__init__(
super(FolderField, self).__init__(
element_type=glare_fields.BlobFieldType, **kwargs)
self.max_blob_size = int(max_blob_size)
self.field_attrs.append('max_blob_size')
self.field_props.append('max_blob_size')
# Classes below added for backward compatibility. They shouldn't be used
Attribute = Field
CompoundAttribute = CompoundField
ListAttribute = ListField
DictAttribute = DictField
BlobAttribute = BlobField
BlobDictAttribute = FolderField

View File

@ -16,15 +16,14 @@
from oslo_versionedobjects import fields
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import fields as glare_fields
from glare.objects.meta import validators
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Blob = attribute.BlobAttribute.init
List = attribute.ListAttribute.init
Dict = attribute.DictAttribute.init
Field = wrappers.Field.init
Blob = wrappers.BlobField.init
List = wrappers.ListField.init
Dict = wrappers.DictField.init
class MuranoPackage(base.BaseArtifact):

View File

@ -16,11 +16,10 @@
from oslo_versionedobjects import fields
from glare.objects import base
from glare.objects.meta import attribute
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Blob = attribute.BlobAttribute.init
Field = wrappers.Field.init
Blob = wrappers.BlobField.init
class TOSCATemplate(base.BaseArtifact):

View File

@ -17,15 +17,15 @@
from oslo_versionedobjects import fields
from glare.objects import base as base_artifact
from glare.objects.meta import attribute
from glare.objects.meta import fields as glare_fields
from glare.objects.meta import validators
from glare.objects.meta import wrappers
Field = attribute.Attribute.init
Dict = attribute.DictAttribute.init
List = attribute.ListAttribute.init
Blob = attribute.BlobAttribute.init
BlobDict = attribute.BlobDictAttribute.init
Field = wrappers.Field.init
Dict = wrappers.DictField.init
List = wrappers.ListField.init
Blob = wrappers.BlobField.init
Folder = wrappers.FolderField.init
class SampleArtifact(base_artifact.BaseArtifact):
@ -42,11 +42,11 @@ class SampleArtifact(base_artifact.BaseArtifact):
required_on_activate=False),
'bool1': Field(fields.FlexibleBooleanField,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,),
filter_ops=(wrappers.FILTER_EQ,),
default=False),
'bool2': Field(fields.FlexibleBooleanField,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,),
filter_ops=(wrappers.FILTER_EQ,),
default=False),
'int1': Field(fields.IntegerField,
required_on_activate=False,
@ -65,31 +65,31 @@ class SampleArtifact(base_artifact.BaseArtifact):
required_on_activate=False),
'list_of_str': List(fields.String,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN)),
filter_ops=(wrappers.FILTER_EQ,
wrappers.FILTER_IN)),
'list_of_int': List(fields.Integer,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN)),
filter_ops=(wrappers.FILTER_EQ,
wrappers.FILTER_IN)),
'dict_of_str': Dict(fields.String,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN)),
filter_ops=(wrappers.FILTER_EQ,
wrappers.FILTER_IN)),
'dict_of_int': Dict(fields.Integer,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,
attribute.FILTER_IN)),
filter_ops=(wrappers.FILTER_EQ,
wrappers.FILTER_IN)),
'dict_of_links': Dict(glare_fields.LinkFieldType,
mutable=True,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,)),
filter_ops=(wrappers.FILTER_EQ,)),
'list_of_links': List(glare_fields.LinkFieldType,
mutable=True,
required_on_activate=False,
filter_ops=(attribute.FILTER_EQ,)),
'dict_of_blobs': BlobDict(required_on_activate=False,
validators=[
validators.MaxDictKeyLen(1000)]),
filter_ops=(wrappers.FILTER_EQ,)),
'dict_of_blobs': Folder(required_on_activate=False,
validators=[
validators.MaxDictKeyLen(1000)]),
'string_mutable': Field(fields.StringField,
required_on_activate=False,
mutable=True),