Merge "Support Nested/Recursive Object Validations"

This commit is contained in:
Jenkins 2014-11-25 14:43:42 +00:00 committed by Gerrit Code Review
commit 4c921878d0
2 changed files with 73 additions and 1 deletions

View File

@ -13,8 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import urlparse
import six
import jsonschema
from designate.openstack.common import log as logging
from designate import exceptions
@ -70,6 +72,18 @@ def make_class_properties(cls):
setattr(cls, field, property(getter, setter))
def _schema_ref_resolver(uri):
"""
Fetches an DesignateObject's schema from a JSON Schema Reference URI
Sample URI: obj://ObjectName#/subpathA/subpathB
"""
obj_name = urlparse.urlsplit(uri).netloc
obj = DesignateObject.obj_cls_from_name(obj_name)
return obj.obj_get_schema()
def make_class_validator(cls):
schema = {
'$schema': 'http://json-schema.org/draft-04/hyper-schema',
@ -87,8 +101,11 @@ def make_class_validator(cls):
if properties.get('required', False):
schema['required'].append(name)
resolver = jsonschema.RefResolver.from_schema(
schema, handlers={'obj': _schema_ref_resolver})
cls._obj_validator = validators.Draft4Validator(
schema, format_checker=format.draft4_format_checker)
schema, resolver=resolver, format_checker=format.draft4_format_checker)
class DesignateObjectMetaclass(type):

View File

@ -51,6 +51,11 @@ class TestValidatableObject(objects.DesignateObject):
},
'required': True,
},
'nested': {
'schema': {
'$ref': 'obj://TestValidatableObject#/'
}
}
}
@ -244,6 +249,26 @@ class DesignateObjectTest(tests.TestCase):
# ID is now a UUID, So - Valid.
self.assertTrue(obj.is_valid)
def test_is_valid_recursive(self):
obj = TestValidatableObject(
id='MyID',
nested=TestValidatableObject(id='MyID'))
# ID should be a UUID, So - Not Valid.
self.assertFalse(obj.is_valid)
# Correct the outer objects ID field
obj.id = 'ffded5c4-e4f6-4e02-a175-48e13c5c12a0'
# Outer ID is now a UUID, Nested ID is Not. So - Invalid.
self.assertFalse(obj.is_valid)
# Correct the nested objects ID field
obj.nested.id = 'ffded5c4-e4f6-4e02-a175-48e13c5c12a0'
# Outer and Nested IDs are now UUIDs. So - Valid.
self.assertTrue(obj.is_valid)
def test_validate(self):
obj = TestValidatableObject()
@ -262,6 +287,36 @@ class DesignateObjectTest(tests.TestCase):
obj.id = 'ffded5c4-e4f6-4e02-a175-48e13c5c12a0'
obj.validate()
def test_validate_recursive(self):
obj = TestValidatableObject(
id='MyID',
nested=TestValidatableObject(id='MyID'))
# ID should be a UUID, So - Invalid.
with testtools.ExpectedException(exceptions.InvalidObject):
obj.validate()
# Correct the outer objects ID field
obj.id = 'ffded5c4-e4f6-4e02-a175-48e13c5c12a0'
# Outer ID is now set, Inner ID is not, still invalid.
e = self.assertRaises(exceptions.InvalidObject, obj.validate)
# Ensure we have exactly one error and fetch it
self.assertEqual(1, len(e.errors))
error = e.errors.pop(0)
# Ensure the format validator has triggered the failure.
self.assertEqual('format', error.validator)
# Ensure the nested ID field has triggered the failure.
self.assertEqual('nested.id', error.absolute_path)
self.assertEqual('nested.id', error.relative_path)
# Set the Nested ID field to a valid value
obj.nested.id = 'ffded5c4-e4f6-4e02-a175-48e13c5c12a0'
obj.validate()
def test_obj_attr_is_set(self):
obj = TestObject()