diff --git a/glare/common/utils.py b/glare/common/utils.py index cbd6aa1..155177a 100644 --- a/glare/common/utils.py +++ b/glare/common/utils.py @@ -40,11 +40,13 @@ from oslo_log import log as logging from oslo_utils import encodeutils from oslo_utils import excutils from oslo_utils import timeutils +from oslo_versionedobjects import fields import six from webob import exc from glare.common import exception from glare.i18n import _, _LE, _LW +from glare.objects.meta import fields as glare_fields CONF = cfg.CONF @@ -553,6 +555,54 @@ class error_handler(object): return new_function +def get_schema_type(attr): + if isinstance(attr, fields.IntegerField): + return 'integer' + elif isinstance(attr, fields.FloatField): + return 'number' + elif isinstance(attr, fields.BooleanField): + return 'boolean' + elif isinstance(attr, glare_fields.List): + return 'array' + elif isinstance(attr, (glare_fields.Dict, glare_fields.BlobField)): + return 'object' + return 'string' + + +def get_glare_type(attr): + if isinstance(attr, fields.IntegerField): + return 'Integer' + elif isinstance(attr, fields.FloatField): + return 'Float' + elif isinstance(attr, fields.FlexibleBooleanField): + return 'Boolean' + elif isinstance(attr, fields.DateTimeField): + return 'DateTime' + elif isinstance(attr, glare_fields.BlobField): + return 'Blob' + elif isinstance(attr, glare_fields.Link): + return 'Link' + elif isinstance(attr, glare_fields.List): + return _get_element_type(attr.element_type) + 'List' + elif isinstance(attr, glare_fields.Dict): + return _get_element_type(attr.element_type) + 'Dict' + return 'String' + + +def _get_element_type(element_type): + if element_type is fields.FlexibleBooleanField: + return 'Boolean' + elif element_type is fields.Integer: + return 'Integer' + elif element_type is fields.Float: + return 'Float' + elif element_type is glare_fields.BlobFieldType: + return 'Blob' + elif element_type is glare_fields.LinkFieldType: + return 'Link' + return 'String' + + class DictDiffer(object): """ Calculate the difference between two dictionaries as: diff --git a/glare/objects/base.py b/glare/objects/base.py index e14dbb6..0353aff 100644 --- a/glare/objects/base.py +++ b/glare/objects/base.py @@ -1087,23 +1087,9 @@ class BaseArtifact(base.VersionedObject): res[key] = val return res - @staticmethod - def schema_type(attr): - if isinstance(attr, fields.IntegerField): - return 'integer' - elif isinstance(attr, fields.FloatField): - return 'number' - elif isinstance(attr, fields.BooleanField): - return 'boolean' - elif isinstance(attr, glare_fields.List): - return 'array' - elif isinstance(attr, (glare_fields.Dict, glare_fields.BlobField)): - return 'object' - return 'string' - @classmethod def schema_attr(cls, attr, attr_name=''): - attr_type = cls.schema_type(attr) + attr_type = utils.get_schema_type(attr) schema = {} # generate schema for validators @@ -1112,6 +1098,7 @@ class BaseArtifact(base.VersionedObject): schema['type'] = (attr_type if not attr.nullable else [attr_type, 'null']) + schema['glareType'] = utils.get_glare_type(attr) output_blob_schema = { 'type': ['object', 'null'], 'properties': { @@ -1134,7 +1121,7 @@ class BaseArtifact(base.VersionedObject): schema['readOnly'] = True if isinstance(attr, glare_fields.Dict): - element_type = (cls.schema_type(attr.element_type) + element_type = (utils.get_schema_type(attr.element_type) if hasattr(attr, 'element_type') else 'string') @@ -1156,7 +1143,7 @@ class BaseArtifact(base.VersionedObject): if attr_type == 'array': schema['items'] = { - 'type': (cls.schema_type(attr.element_type) + 'type': (utils.get_schema_type(attr.element_type) if hasattr(attr, 'element_type') else 'string')} diff --git a/glare/objects/heat_template.py b/glare/objects/heat_template.py index 83b03e1..e5cc9d5 100644 --- a/glare/objects/heat_template.py +++ b/glare/objects/heat_template.py @@ -29,7 +29,7 @@ BlobDict = attribute.BlobDictAttribute.init class HeatTemplate(base.BaseArtifact): fields = { - 'environments': Dict(glare_fields.Link, + 'environments': Dict(glare_fields.LinkFieldType, mutable=True, description="References to Heat Environments " "that can be used with current " diff --git a/glare/objects/murano_package.py b/glare/objects/murano_package.py index d9f0347..b97f233 100644 --- a/glare/objects/murano_package.py +++ b/glare/objects/murano_package.py @@ -50,7 +50,7 @@ class MuranoPackage(base.BaseArtifact): "the package."), 'inherits': Dict(fields.String), 'keywords': List(fields.String, mutable=True), - 'dependencies': List(glare_fields.Link, + 'dependencies': List(glare_fields.LinkFieldType, required_on_activate=False, description="List of package dependencies for " "this package."), diff --git a/glare/tests/functional/base.py b/glare/tests/functional/base.py index e6a68e9..18d40b4 100644 --- a/glare/tests/functional/base.py +++ b/glare/tests/functional/base.py @@ -27,7 +27,7 @@ def sort_results(lst, target='name'): class TestArtifact(functional.FunctionalTest): enabled_types = (u'sample_artifact', u'images', u'heat_templates', u'heat_environments', u'tosca_templates', - u'murano_packages') + u'murano_packages', u'all') users = { 'user1': { diff --git a/glare/tests/functional/test_schemas.py b/glare/tests/functional/test_schemas.py index 2006ae7..dcd838c 100644 --- a/glare/tests/functional/test_schemas.py +++ b/glare/tests/functional/test_schemas.py @@ -29,6 +29,7 @@ fixture_base_props = { u'lt', u'lte'], u'format': u'date-time', + u'glareType': u'DateTime', u'readOnly': True, u'required_on_activate': False, u'sortable': True, @@ -44,6 +45,7 @@ fixture_base_props = { u'lt', u'lte'], u'format': u'date-time', + u'glareType': u'DateTime', u'readOnly': True, u'sortable': True, u'type': u'string'}, @@ -52,6 +54,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 4096, u'mutable': True, u'required_on_activate': False, @@ -60,6 +63,7 @@ fixture_base_props = { u'icon': {u'additionalProperties': False, u'description': u'Artifact icon.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': {u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, u'sha256': {u'type': [u'string', u'null']}, @@ -83,6 +87,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'pattern': u'^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}' u'-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$', @@ -93,6 +98,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', @@ -101,6 +107,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', @@ -111,6 +118,7 @@ fixture_base_props = { u'about an artifact.', u'filter_ops': [u'eq', u'neq'], + u'glareType': u'StringDict', u'maxProperties': 255, u'required_on_activate': False, u'type': [u'object', @@ -119,6 +127,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'sortable': True, @@ -127,6 +136,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'readOnly': True, u'required_on_activate': False, @@ -137,6 +147,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringDict', u'maxProperties': 255, u'properties': {u'company': {u'type': u'string'}, u'href': {u'type': u'string'}, @@ -151,6 +162,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': {u'type': u'string'}, u'maxItems': 255, u'required_on_activate': False, @@ -166,6 +178,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'sortable': True, u'type': u'string'}, u'supported_by': {u'additionalProperties': {u'type': u'string'}, @@ -174,6 +187,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringDict', u'maxProperties': 255, u'required': [u'name'], u'required_on_activate': False, @@ -184,6 +198,7 @@ fixture_base_props = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': {u'type': u'string'}, u'maxItems': 255, u'mutable': True, @@ -200,6 +215,7 @@ fixture_base_props = { u'lt', u'lte'], u'format': u'date-time', + u'glareType': u'DateTime', u'readOnly': True, u'sortable': True, u'type': u'string'}, @@ -212,6 +228,7 @@ fixture_base_props = { u'gte', u'lt', u'lte'], + u'glareType': u'String', u'pattern': u'/^([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-' u'([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?' u'(?:\\+[0-9A-Za-z-]+)?$/', @@ -223,6 +240,7 @@ fixture_base_props = { u'artifact can be available to other ' u'users.', u'filter_ops': [u'eq'], + u'glareType': u'String', u'maxLength': 255, u'sortable': True, u'type': u'string'} @@ -241,6 +259,7 @@ fixtures = { u'blob': {u'additionalProperties': False, u'description': u'I am Blob', u'filter_ops': [], + u'glareType': u'Blob', u'mutable': True, u'properties': { u'md5': {u'type': [u'string', u'null']}, @@ -269,23 +288,27 @@ fixtures = { u'null']}, u'bool1': {u'default': False, u'filter_ops': [u'eq'], + u'glareType': u'Boolean', u'required_on_activate': False, u'type': [u'string', u'null']}, u'bool2': {u'default': False, u'filter_ops': [u'eq'], + u'glareType': u'Boolean', u'required_on_activate': False, u'type': [u'string', u'null']}, u'link1': {u'filter_ops': [u'eq', u'neq', u'in'], + 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'glareType': u'Link', u'required_on_activate': False, u'type': [u'string', u'null']}, @@ -319,6 +342,7 @@ fixtures = { u'null']}, u'default': {}, u'filter_ops': [], + u'glareType': u'BlobDict', u'maxProperties': 255, u'required_on_activate': False, u'type': [u'object', @@ -328,6 +352,7 @@ fixtures = { u'type': u'string'}, u'default': {}, u'filter_ops': [u'eq'], + u'glareType': u'IntegerDict', u'maxProperties': 255, u'required_on_activate': False, u'type': [u'object', @@ -337,6 +362,7 @@ fixtures = { u'type': u'string'}, u'default': {}, u'filter_ops': [u'eq'], + u'glareType': u'StringDict', u'maxProperties': 255, u'required_on_activate': False, u'type': [u'object', @@ -346,6 +372,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringDict', u'maxProperties': 3, u'properties': { u'abc': {u'type': [u'string', @@ -366,6 +393,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'Float', u'required_on_activate': False, u'sortable': True, u'type': [u'number', @@ -377,6 +405,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'Float', u'required_on_activate': False, u'sortable': True, u'type': [u'number', @@ -388,6 +417,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'Integer', u'required_on_activate': False, u'sortable': True, u'type': [u'integer', @@ -399,6 +429,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'Integer', u'required_on_activate': False, u'sortable': True, u'type': [u'integer', @@ -410,6 +441,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'Integer', u'maximum': 20, u'minimum': 10, u'required_on_activate': False, @@ -417,6 +449,7 @@ fixtures = { u'null']}, u'list_of_int': {u'default': [], u'filter_ops': [u'eq'], + u'glareType': u'IntegerList', u'items': { u'type': u'string'}, u'maxItems': 255, @@ -425,6 +458,7 @@ fixtures = { u'null']}, u'list_of_str': {u'default': [], u'filter_ops': [u'eq'], + u'glareType': u'StringList', u'items': { u'type': u'string'}, u'maxItems': 255, @@ -436,6 +470,7 @@ fixtures = { u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': { u'type': u'string'}, u'maxItems': 3, @@ -445,6 +480,7 @@ fixtures = { u'unique': True}, u'small_blob': {u'additionalProperties': False, u'filter_ops': [], + u'glareType': u'Blob', u'mutable': True, u'properties': { u'md5': {u'type': [u'string', u'null']}, @@ -479,6 +515,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'sortable': True, @@ -491,6 +528,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'String', u'maxLength': 255, u'mutable': True, u'required_on_activate': False, @@ -504,6 +542,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']}, @@ -519,6 +558,7 @@ fixtures = { u'gte', u'lt', u'lte'], + u'glareType': u'String', u'maxLength': 10, u'required_on_activate': False, u'type': [u'string', @@ -527,6 +567,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'readOnly': True, u'sortable': True, @@ -544,6 +585,7 @@ fixtures = { u'additionalProperties': False, u'description': u'TOSCA template body.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': { u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, @@ -568,6 +610,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']}, @@ -586,6 +629,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': {u'type': u'string'}, u'maxItems': 255, u'mutable': True, @@ -598,6 +642,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': {u'type': u'string'}, u'maxItems': 255, u'type': [u'array', @@ -610,6 +655,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'LinkList', u'items': {u'type': u'string'}, u'maxItems': 255, u'required_on_activate': False, @@ -620,6 +666,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'mutable': True, u'type': [u'string', @@ -630,6 +677,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringDict', u'maxProperties': 255, u'type': [u'object', u'null']}, @@ -637,6 +685,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringList', u'items': {u'type': u'string'}, u'maxItems': 255, u'mutable': True, @@ -646,6 +695,7 @@ fixtures = { u'additionalProperties': False, u'description': u'Murano Package binary.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': {u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, u'sha256': {u'type': [u'string', u'null']}, @@ -674,6 +724,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']} @@ -693,6 +744,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', @@ -701,6 +753,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', u'null']}, @@ -716,6 +769,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']}, @@ -735,11 +789,13 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']}, u'image': {u'additionalProperties': False, u'description': u'Image binary.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': { u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, @@ -769,6 +825,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', u'null']}, @@ -780,6 +837,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', @@ -791,6 +849,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'pattern': u'^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-' u'([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-' @@ -802,6 +861,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'Integer', u'minimum': 0, u'required_on_activate': False, u'type': [u'integer', u'null']}, @@ -810,6 +870,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'Integer', u'minimum': 0, u'required_on_activate': False, u'type': [u'integer', u'null']}, @@ -821,6 +882,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', u'null']}, @@ -830,6 +892,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'required_on_activate': False, u'type': [u'string', u'null']}, @@ -840,6 +903,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'pattern': u'^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F])' u'{4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$', @@ -861,6 +925,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'StringDict', u'maxProperties': 255, u'mutable': True, u'type': [u'object', @@ -874,6 +939,7 @@ fixtures = { u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'LinkDict', u'maxProperties': 255, u'mutable': True, u'type': [u'object', @@ -906,6 +972,7 @@ fixtures = { u'name of template and value is nested ' u'template body.', u'filter_ops': [], + u'glareType': u'BlobDict', u'maxProperties': 255, u'type': [u'object', u'null']}, @@ -913,6 +980,7 @@ fixtures = { u'additionalProperties': False, u'description': u'Heat template body.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': { u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, @@ -946,6 +1014,7 @@ fixtures = { u'additionalProperties': False, u'description': u'Heat Environment text body.', u'filter_ops': [], + u'glareType': u'Blob', u'properties': {u'md5': {u'type': [u'string', u'null']}, u'sha1': {u'type': [u'string', u'null']}, u'sha256': {u'type': [u'string', u'null']}, @@ -975,6 +1044,7 @@ fixtures = { u'properties': generate_type_props({ u'type_name': {u'description': u'Name of artifact type.', u'filter_ops': [u'eq', u'neq', u'in'], + u'glareType': u'String', u'maxLength': 255, u'type': [u'string', u'null']}, @@ -993,22 +1063,13 @@ class TestSchemas(base.TestArtifact): result = self.get(url='/schemas/%s' % at) self.assertEqual(fixtures[at], result['schemas'][at], utils.DictDiffer( - result['schemas'][at]['properties'], - fixtures[at]['properties'])) + fixtures[at]['properties'], + result['schemas'][at]['properties'])) # Get list schemas of artifacts result = self.get(url='/schemas') self.assertEqual(fixtures, result['schemas'], utils.DictDiffer( - result['schemas'], fixtures)) - - # Get schema of sample_artifact - result = self.get(url='/schemas/sample_artifact') - self.assertEqual(fixtures['sample_artifact'], - result['schemas']['sample_artifact'], - utils.DictDiffer( - result['schemas']['sample_artifact'][ - 'properties'], - fixtures['sample_artifact']['properties'])) + fixtures, result['schemas'])) # Validation of schemas result = self.get(url='/schemas')['schemas']