API attribute processing: add 'default_overrides_none'
This change introduces an 'default_overrides_none' flag. When enabled, the default value for the attribute will be used, including if the attribute was explicitly defined as None. This is introduced to support the current behavior of the flowclassifier API extension from networking-sfc (see Id5aea04553b78dd17fca85de88cf076b42d24140). Change-Id: I511d7dfd02ceed417d377451ec0265d9704ef261
This commit is contained in:
parent
7c4e74a490
commit
478c4d85b0
|
@ -77,20 +77,21 @@ that the subnet does not have a gateway IP.
|
|||
|
||||
The following are the defined keys for attribute maps:
|
||||
|
||||
====================== ======
|
||||
``default`` default value of the attribute (if missing, the attribute becomes mandatory)
|
||||
``allow_post`` the attribute can be used on ``POST`` requests
|
||||
``allow_put`` the attribute can be used on ``PUT`` requests
|
||||
``validate`` specifies rules for validating data in the attribute
|
||||
``convert_to`` transformation to apply to the value before it is returned
|
||||
``convert_list_to`` if the value is a list, apply this transformation to the value before it is returned
|
||||
``is_filter`` the attribute can be used in ``GET`` requests as filter
|
||||
``is_sort_key`` the attribute can be used in ``GET`` requests as sort_key
|
||||
``is_visible`` the attribute is returned in ``GET`` responses
|
||||
``required_by_policy`` the attribute is required by the policy engine and should therefore be filled by the API layer even if not present in request body
|
||||
``enforce_policy`` the attribute is actively part of the policy enforcing mechanism, ie: there might be rules which refer to this attribute
|
||||
``primary_key`` Mark the attribute as a unique key.
|
||||
====================== ======
|
||||
========================== ======
|
||||
``default`` default value of the attribute (if missing, the attribute becomes mandatory)
|
||||
``allow_post`` the attribute can be used on ``POST`` requests
|
||||
``allow_put`` the attribute can be used on ``PUT`` requests
|
||||
``validate`` specifies rules for validating data in the attribute
|
||||
``convert_to`` transformation to apply to the value before it is returned
|
||||
``convert_list_to`` if the value is a list, apply this transformation to the value before it is returned
|
||||
``is_filter`` the attribute can be used in ``GET`` requests as filter
|
||||
``is_sort_key`` the attribute can be used in ``GET`` requests as sort_key
|
||||
``is_visible`` the attribute is returned in ``GET`` responses
|
||||
``required_by_policy`` the attribute is required by the policy engine and should therefore be filled by the API layer even if not present in request body
|
||||
``enforce_policy`` the attribute is actively part of the policy enforcing mechanism, ie: there might be rules which refer to this attribute
|
||||
``primary_key`` Mark the attribute as a unique key.
|
||||
``default_overrides_none`` if set, if the value passed is None, it will be replaced by the ``default`` value
|
||||
========================== ======
|
||||
|
||||
When extending existing sub-resources, the sub-attribute map must define all
|
||||
extension attributes under the ``parameters`` object. This instructs the API
|
||||
|
|
|
@ -58,6 +58,18 @@ def populate_project_info(attributes):
|
|||
return attributes
|
||||
|
||||
|
||||
def _fill_default(res_dict, attr_name, attr_spec):
|
||||
# update res_dict[attr_name] record with the default value
|
||||
# specified in attr_spec, taking into account default_overrides_none
|
||||
if attr_spec.get('default_overrides_none'):
|
||||
if res_dict.get(attr_name) is None:
|
||||
res_dict[attr_name] = attr_spec.get('default')
|
||||
return
|
||||
|
||||
res_dict[attr_name] = res_dict.get(attr_name,
|
||||
attr_spec.get('default'))
|
||||
|
||||
|
||||
class AttributeInfo(object):
|
||||
"""Provides operations on a resource's attribute map.
|
||||
|
||||
|
@ -114,8 +126,7 @@ class AttributeInfo(object):
|
|||
msg = _("Failed to parse request. Required "
|
||||
"attribute '%s' not specified") % attr
|
||||
raise exc_cls(msg)
|
||||
res_dict[attr] = res_dict.get(attr,
|
||||
attr_vals.get('default'))
|
||||
_fill_default(res_dict, attr, attr_vals)
|
||||
elif check_allow_post:
|
||||
if attr in res_dict:
|
||||
msg = _("Attribute '%s' not allowed in POST") % attr
|
||||
|
|
|
@ -152,4 +152,5 @@ KNOWN_KEYWORDS = (
|
|||
'primary_key',
|
||||
'required_by_policy',
|
||||
'validate',
|
||||
'default_overrides_none',
|
||||
)
|
||||
|
|
|
@ -64,6 +64,7 @@ ASSERT_FUNCTIONS = {
|
|||
'primary_key': assert_true,
|
||||
'required_by_policy': assert_bool,
|
||||
'validate': assert_validator,
|
||||
'default_overrides_none': assert_bool,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -138,6 +138,17 @@ class TestAttributeInfo(base.BaseTestCase):
|
|||
self.assertRaises(self._EXC_CLS, attr_inst.fill_post_defaults,
|
||||
{'key': 'X'}, self._EXC_CLS)
|
||||
|
||||
def test_fill_none_overridden_by_default(self):
|
||||
attr_info = {
|
||||
'key': {
|
||||
'allow_post': True,
|
||||
'default': 42,
|
||||
'default_overrides_none': True,
|
||||
},
|
||||
}
|
||||
attr_inst = attributes.AttributeInfo(attr_info)
|
||||
self._test_fill_default_value(attr_inst, {'key': 42}, {'key': None})
|
||||
|
||||
def _test_convert_value(self, attr_inst, expected, res_dict):
|
||||
attr_inst.convert_values(res_dict)
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
A new flag can be used in API definition: ``default_overrides_none``.
|
||||
When enabled, the default value for the attribute will
|
||||
be used, including if the attribute was explicitly defined
|
||||
as ``null``.
|
Loading…
Reference in New Issue