netsted template validate type error
[parser.git] / tosca2heat / tosca-parser / toscaparser / dataentity.py
1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
2 #    not use this file except in compliance with the License. You may obtain
3 #    a copy of the License at
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
7 #    Unless required by applicable law or agreed to in writing, software
8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 #    License for the specific language governing permissions and limitations
11 #    under the License.
12
13 from toscaparser.common.exception import ExceptionCollector
14 from toscaparser.common.exception import MissingRequiredFieldError
15 from toscaparser.common.exception import TypeMismatchError
16 from toscaparser.common.exception import UnknownFieldError
17 from toscaparser.elements.constraints import Schema
18 from toscaparser.elements.datatype import DataType
19 from toscaparser.elements.scalarunit import ScalarUnit_Frequency
20 from toscaparser.elements.scalarunit import ScalarUnit_Size
21 from toscaparser.elements.scalarunit import ScalarUnit_Time
22
23 from toscaparser.utils.gettextutils import _
24 from toscaparser.utils import validateutils
25
26
27 class DataEntity(object):
28     '''A complex data value entity.'''
29
30     def __init__(self, datatypename, value_dict, custom_def=None):
31         self.custom_def = custom_def
32         self.datatype = DataType(datatypename, custom_def)
33         self.schema = self.datatype.get_all_properties()
34         self.value = value_dict
35
36     def validate(self):
37         '''Validate the value by the definition of the datatype.'''
38
39         # A datatype can not have both 'type' and 'properties' definitions.
40         # If the datatype has 'type' definition
41         if self.datatype.value_type:
42             self.value = DataEntity.validate_datatype(self.datatype.value_type,
43                                                       self.value,
44                                                       None,
45                                                       self.custom_def)
46             schema = Schema(None, self.datatype.defs)
47             for constraint in schema.constraints:
48                 constraint.validate(self.value)
49         # If the datatype has 'properties' definition
50         else:
51             if not isinstance(self.value, dict):
52                 ExceptionCollector.appendException(
53                     TypeMismatchError(what=self.value,
54                                       type=self.datatype.type))
55             allowed_props = []
56             required_props = []
57             default_props = {}
58             if self.schema:
59                 allowed_props = self.schema.keys()
60                 for name, prop_def in self.schema.items():
61                     if prop_def.required:
62                         required_props.append(name)
63                     if prop_def.default:
64                         default_props[name] = prop_def.default
65
66             # check allowed field
67             for value_key in list(self.value.keys()):
68                 if value_key not in allowed_props:
69                     ExceptionCollector.appendException(
70                         UnknownFieldError(what=(_('Data value of type "%s"')
71                                                 % self.datatype.type),
72                                           field=value_key))
73
74             # check default field
75             for def_key, def_value in list(default_props.items()):
76                 if def_key not in list(self.value.keys()):
77                     self.value[def_key] = def_value
78
79             # check missing field
80             missingprop = []
81             for req_key in required_props:
82                 if req_key not in list(self.value.keys()):
83                     missingprop.append(req_key)
84             if missingprop:
85                 ExceptionCollector.appendException(
86                     MissingRequiredFieldError(
87                         what=(_('Data value of type "%s"')
88                               % self.datatype.type), required=missingprop))
89
90             # check every field
91             for name, value in list(self.value.items()):
92                 prop_schema = Schema(name, self._find_schema(name))
93                 # check if field value meets type defined
94                 DataEntity.validate_datatype(prop_schema.type, value,
95                                              prop_schema.entry_schema,
96                                              self.custom_def)
97                 # check if field value meets constraints defined
98                 if prop_schema.constraints:
99                     for constraint in prop_schema.constraints:
100                         if isinstance(value, list):
101                             for val in value:
102                                 constraint.validate(val)
103                         else:
104                             constraint.validate(value)
105
106         return self.value
107
108     def _find_schema(self, name):
109         if self.schema and name in self.schema.keys():
110             return self.schema[name].schema
111
112     @staticmethod
113     def validate_datatype(type, value, entry_schema=None, custom_def=None):
114         '''Validate value with given type.
115
116         If type is list or map, validate its entry by entry_schema(if defined)
117         If type is a user-defined complex datatype, custom_def is required.
118         '''
119         if type == Schema.STRING:
120             return validateutils.validate_string(value)
121         elif type == Schema.INTEGER:
122             return validateutils.validate_integer(value)
123         elif type == Schema.FLOAT:
124             return validateutils.validate_float(value)
125         elif type == Schema.NUMBER:
126             return validateutils.validate_number(value)
127         elif type == Schema.BOOLEAN:
128             return validateutils.validate_boolean(value)
129         elif type == Schema.RANGE:
130             return validateutils.validate_range(value)
131         elif type == Schema.TIMESTAMP:
132             validateutils.validate_timestamp(value)
133             return value
134         elif type == Schema.LIST:
135             validateutils.validate_list(value)
136             if entry_schema:
137                 DataEntity.validate_entry(value, entry_schema, custom_def)
138             return value
139         elif type == Schema.SCALAR_UNIT_SIZE:
140             return ScalarUnit_Size(value).validate_scalar_unit()
141         elif type == Schema.SCALAR_UNIT_FREQUENCY:
142             return ScalarUnit_Frequency(value).validate_scalar_unit()
143         elif type == Schema.SCALAR_UNIT_TIME:
144             return ScalarUnit_Time(value).validate_scalar_unit()
145         elif type == Schema.VERSION:
146             return validateutils.TOSCAVersionProperty(value).get_version()
147         elif type == Schema.MAP:
148             validateutils.validate_map(value)
149             if entry_schema:
150                 DataEntity.validate_entry(value, entry_schema, custom_def)
151             return value
152         else:
153             data = DataEntity(type, value, custom_def)
154             return data.validate()
155
156     @staticmethod
157     def validate_entry(value, entry_schema, custom_def=None):
158         '''Validate entries for map and list.'''
159         schema = Schema(None, entry_schema)
160         valuelist = value
161         if isinstance(value, dict):
162             valuelist = list(value.values())
163         for v in valuelist:
164             DataEntity.validate_datatype(schema.type, v, schema.entry_schema,
165                                          custom_def)
166             if schema.constraints:
167                 for constraint in schema.constraints:
168                     constraint.validate(v)
169         return value