Added support for range type
The current code is missing support for handling range type which is used is in PortSpec. Hence added support for it Change-Id: I97bf1c68280dce3dba01938790ff4f4aee86dc1a Closes-Bug: #1528164
This commit is contained in:
parent
0a0abe5b42
commit
9f3e954a89
@ -97,7 +97,11 @@ class DataEntity(object):
|
||||
# check if field value meets constraints defined
|
||||
if prop_schema.constraints:
|
||||
for constraint in prop_schema.constraints:
|
||||
constraint.validate(value)
|
||||
if isinstance(value, list):
|
||||
for val in value:
|
||||
constraint.validate(val)
|
||||
else:
|
||||
constraint.validate(value)
|
||||
|
||||
return self.value
|
||||
|
||||
@ -122,6 +126,8 @@ class DataEntity(object):
|
||||
return validateutils.validate_number(value)
|
||||
elif type == Schema.BOOLEAN:
|
||||
return validateutils.validate_boolean(value)
|
||||
elif type == Schema.RANGE:
|
||||
return validateutils.validate_range(value)
|
||||
elif type == Schema.TIMESTAMP:
|
||||
validateutils.validate_timestamp(value)
|
||||
return value
|
||||
|
@ -33,12 +33,12 @@ class Schema(collections.Mapping):
|
||||
)
|
||||
|
||||
PROPERTY_TYPES = (
|
||||
INTEGER, STRING, BOOLEAN, FLOAT,
|
||||
INTEGER, STRING, BOOLEAN, FLOAT, RANGE,
|
||||
NUMBER, TIMESTAMP, LIST, MAP,
|
||||
SCALAR_UNIT_SIZE, SCALAR_UNIT_FREQUENCY, SCALAR_UNIT_TIME,
|
||||
PORTDEF, VERSION
|
||||
) = (
|
||||
'integer', 'string', 'boolean', 'float',
|
||||
'integer', 'string', 'boolean', 'float', 'range',
|
||||
'number', 'timestamp', 'list', 'map',
|
||||
'scalar-unit.size', 'scalar-unit.frequency', 'scalar-unit.time',
|
||||
'PortDef', 'version'
|
||||
@ -127,6 +127,8 @@ class Constraint(object):
|
||||
'less_or_equal', 'in_range', 'valid_values', 'length',
|
||||
'min_length', 'max_length', 'pattern')
|
||||
|
||||
UNBOUNDED = 'UNBOUNDED'
|
||||
|
||||
def __new__(cls, property_name, property_type, constraint):
|
||||
if cls is not Constraint:
|
||||
return super(Constraint, cls).__new__(cls)
|
||||
@ -372,11 +374,11 @@ class InRange(Constraint):
|
||||
constraint_key = Constraint.IN_RANGE
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
datetime.time, datetime.datetime, str)
|
||||
|
||||
valid_prop_types = (Schema.INTEGER, Schema.FLOAT, Schema.TIMESTAMP,
|
||||
Schema.SCALAR_UNIT_SIZE, Schema.SCALAR_UNIT_FREQUENCY,
|
||||
Schema.SCALAR_UNIT_TIME)
|
||||
Schema.SCALAR_UNIT_TIME, Schema.RANGE)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(InRange, self).__init__(property_name, property_type, constraint)
|
||||
@ -391,16 +393,27 @@ class InRange(Constraint):
|
||||
ExceptionCollector.appendException(
|
||||
InvalidSchemaError(_('The property "in_range" expects '
|
||||
'comparable values.')))
|
||||
# The only string we allow for range is the special value
|
||||
# 'UNBOUNDED'
|
||||
if(isinstance(value, str) and value != self.UNBOUNDED):
|
||||
ExceptionCollector.appendException(
|
||||
InvalidSchemaError(_('The property "in_range" expects '
|
||||
'comparable values.')))
|
||||
|
||||
self.min = self.constraint_value[0]
|
||||
self.max = self.constraint_value[1]
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value < self.min:
|
||||
if not isinstance(self.min, str):
|
||||
if value < self.min:
|
||||
return False
|
||||
elif self.min != self.UNBOUNDED:
|
||||
return False
|
||||
if value > self.max:
|
||||
if not isinstance(self.max, str):
|
||||
if value > self.max:
|
||||
return False
|
||||
elif self.max != self.UNBOUNDED:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _err_msg(self, value):
|
||||
|
@ -61,6 +61,13 @@ class DataTypeTest(TestCase):
|
||||
type: string
|
||||
contact_phone:
|
||||
type: string
|
||||
|
||||
tosca.my.datatypes.TestLab:
|
||||
properties:
|
||||
temperature:
|
||||
type: range
|
||||
constraints:
|
||||
- in_range: [-256, UNBOUNDED]
|
||||
'''
|
||||
custom_type_def = yamlparser.simple_parse(custom_type_schema)
|
||||
|
||||
@ -325,3 +332,48 @@ class DataTypeTest(TestCase):
|
||||
self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path)
|
||||
exception.ExceptionCollector.assertExceptionMessage(
|
||||
ValueError, _('"123456789" is not a string.'))
|
||||
|
||||
def test_valid_range_type(self):
|
||||
value_snippet = '''
|
||||
user_port:
|
||||
protocol: tcp
|
||||
target_range: [20000, 60000]
|
||||
source_range: [1000, 3000]
|
||||
'''
|
||||
value = yamlparser.simple_parse(value_snippet)
|
||||
data = DataEntity('PortSpec', value.get('user_port'))
|
||||
self.assertIsNotNone(data.validate())
|
||||
|
||||
def test_invalid_range_datatype(self):
|
||||
value_snippet = '''
|
||||
user_port:
|
||||
protocol: tcp
|
||||
target_range: [20000]
|
||||
'''
|
||||
value = yamlparser.simple_parse(value_snippet)
|
||||
data = DataEntity('PortSpec', value.get('user_port'))
|
||||
err = self.assertRaises(ValueError, data.validate)
|
||||
self.assertEqual(_('"[20000]" is not a valid range.'
|
||||
),
|
||||
err.__str__())
|
||||
|
||||
value_snippet = '''
|
||||
user_port:
|
||||
protocol: tcp
|
||||
target_range: [20000, 3000]
|
||||
'''
|
||||
value = yamlparser.simple_parse(value_snippet)
|
||||
data = DataEntity('PortSpec', value.get('user_port'))
|
||||
err = self.assertRaises(ValueError, data.validate)
|
||||
self.assertEqual(_('"[20000, 3000]" is not a valid range.'
|
||||
),
|
||||
err.__str__())
|
||||
|
||||
def test_range_unbounded(self):
|
||||
value_snippet = '''
|
||||
temperature: [-100, 999999]
|
||||
'''
|
||||
value = yamlparser.simple_parse(value_snippet)
|
||||
data = DataEntity('tosca.my.datatypes.TestLab', value,
|
||||
DataTypeTest.custom_type_def)
|
||||
self.assertIsNotNone(data.validate())
|
||||
|
@ -68,6 +68,18 @@ def validate_list(value):
|
||||
return value
|
||||
|
||||
|
||||
def validate_range(value):
|
||||
validate_list(value)
|
||||
if isinstance(value, list):
|
||||
if len(value) != 2 or not (value[0] <= value[1]):
|
||||
ExceptionCollector.appendException(
|
||||
ValueError(_('"%s" is not a valid range.') % value))
|
||||
validate_integer(value[0])
|
||||
if not value[1] == "UNBOUNDED":
|
||||
validate_integer(value[1])
|
||||
return value
|
||||
|
||||
|
||||
def validate_map(value):
|
||||
if not isinstance(value, collections.Mapping):
|
||||
ExceptionCollector.appendException(
|
||||
|
Loading…
x
Reference in New Issue
Block a user