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:
Thomas Morin 2018-03-26 12:07:30 +02:00
parent 7c4e74a490
commit 478c4d85b0
6 changed files with 48 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -152,4 +152,5 @@ KNOWN_KEYWORDS = (
'primary_key',
'required_by_policy',
'validate',
'default_overrides_none',
)

View File

@ -64,6 +64,7 @@ ASSERT_FUNCTIONS = {
'primary_key': assert_true,
'required_by_policy': assert_bool,
'validate': assert_validator,
'default_overrides_none': assert_bool,
}

View File

@ -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)

View File

@ -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``.