Merge "Add MappedListField"
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds a new field called ``MappedListField`` which supports a list of
|
||||
mapped instances.
|
||||
@@ -258,6 +258,52 @@ class MappedField(Field):
|
||||
adapter=mapping.get)
|
||||
|
||||
|
||||
class MappedListField(Field):
|
||||
"""Field taking a list of values with a mapping for the values
|
||||
|
||||
Given JSON {'field':['xxx', 'yyy']}, a sushy resource definition and
|
||||
mapping {'xxx':'a', 'yyy':'b'}, the sushy object to come out will be like
|
||||
resource.field = ['a', 'b']
|
||||
"""
|
||||
|
||||
def __init__(self, field, mapping, required=False, default=None):
|
||||
"""Create a mapped list field definition.
|
||||
|
||||
:param field: JSON field to fetch the list of values from.
|
||||
:param mapping: a mapping for the list elements.
|
||||
:param required: whether this field is required. Missing required
|
||||
fields result in MissingAttributeError.
|
||||
:param default: the default value to use when the field is missing.
|
||||
Only has effect when the field is not required.
|
||||
"""
|
||||
if not isinstance(mapping, collectionsAbc.Mapping):
|
||||
raise TypeError("The mapping argument must be a mapping")
|
||||
|
||||
self._mapping_adapter = mapping.get
|
||||
super(MappedListField, self).__init__(
|
||||
field, required=required, default=default,
|
||||
adapter=lambda x: x)
|
||||
|
||||
def _load(self, body, resource, nested_in=None):
|
||||
"""Load the mapped list.
|
||||
|
||||
:param body: parent JSON body.
|
||||
:param resource: parent resource.
|
||||
:param nested_in: parent resource name (for error reporting only).
|
||||
:returns: a new list object containing the mapped values.
|
||||
"""
|
||||
nested_in = (nested_in or []) + self._path
|
||||
values = super(MappedListField, self)._load(body, resource)
|
||||
|
||||
if values is None:
|
||||
return
|
||||
|
||||
instances = [self._mapping_adapter(value) for value in values
|
||||
if self._mapping_adapter(value) is not None]
|
||||
|
||||
return instances
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AbstractJsonReader(object):
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import logging
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources import mappings as res_maps
|
||||
from sushy.resources.system.storage import drive
|
||||
from sushy.resources.system.storage import volume
|
||||
from sushy import utils
|
||||
@@ -43,12 +44,12 @@ class StorageControllersListField(base.ListField):
|
||||
speed_gbps = base.Field('SpeedGbps')
|
||||
"""The maximum speed of the storage controller's device interface."""
|
||||
|
||||
controller_protocols = base.Field('SupportedControllerProtocols',
|
||||
adapter=list)
|
||||
controller_protocols = base.MappedListField(
|
||||
'SupportedControllerProtocols', res_maps.PROTOCOL_TYPE_VALUE_MAP)
|
||||
"""The protocols by which this storage controller can be communicated to"""
|
||||
|
||||
device_protocols = base.Field('SupportedDeviceProtocols',
|
||||
adapter=list)
|
||||
device_protocols = base.MappedListField('SupportedDeviceProtocols',
|
||||
res_maps.PROTOCOL_TYPE_VALUE_MAP)
|
||||
"""The protocols which the controller can use tocommunicate with devices"""
|
||||
|
||||
|
||||
|
||||
@@ -122,8 +122,10 @@ class StorageTestCase(base.TestCase):
|
||||
identifier.durable_name_format)
|
||||
self.assertEqual('345C59DBD970859C', identifier.durable_name)
|
||||
self.assertEqual(12, controller.speed_gbps)
|
||||
self.assertEqual(["PCIe"], controller.controller_protocols)
|
||||
self.assertEqual(["SAS", "SATA"], controller.device_protocols)
|
||||
self.assertEqual([sushy.PROTOCOL_TYPE_PCIe],
|
||||
controller.controller_protocols)
|
||||
self.assertEqual([sushy.PROTOCOL_TYPE_SAS, sushy.PROTOCOL_TYPE_SATA],
|
||||
controller.device_protocols)
|
||||
|
||||
def test_drives_after_refresh(self):
|
||||
self.storage.refresh()
|
||||
|
||||
@@ -294,7 +294,7 @@ class ResourceCollectionBaseTestCase(base.TestCase):
|
||||
TEST_JSON = {
|
||||
'String': 'a string',
|
||||
'Integer': '42',
|
||||
'List': ['a string', 42],
|
||||
'MappedList': ['raw1', 'raw2', 'raw'],
|
||||
'Nested': {
|
||||
'String': 'another string',
|
||||
'Integer': 0,
|
||||
@@ -321,7 +321,9 @@ TEST_JSON = {
|
||||
|
||||
|
||||
MAPPING = {
|
||||
'raw': 'real'
|
||||
'raw': 'real',
|
||||
'raw1': 'real1',
|
||||
'raw2': 'real2'
|
||||
}
|
||||
|
||||
|
||||
@@ -347,6 +349,7 @@ class ComplexResource(resource_base.ResourceBase):
|
||||
string = resource_base.Field('String', required=True)
|
||||
integer = resource_base.Field('Integer', adapter=int)
|
||||
nested = NestedTestField('Nested')
|
||||
mapped_list = resource_base.MappedListField('MappedList', MAPPING)
|
||||
field_list = TestListField('ListField')
|
||||
dictionary = TestDictionaryField('Dictionary')
|
||||
non_existing_nested = NestedTestField('NonExistingNested')
|
||||
@@ -371,6 +374,8 @@ class FieldTestCase(base.TestCase):
|
||||
self.assertEqual('field value', self.test_resource.nested.nested_field)
|
||||
self.assertEqual('real', self.test_resource.nested.mapped)
|
||||
self.assertEqual(3.14, self.test_resource.nested.non_existing)
|
||||
self.assertEqual(['real1', 'real2', 'real'],
|
||||
self.test_resource.mapped_list)
|
||||
self.assertEqual('a third string',
|
||||
self.test_resource.field_list[0].string)
|
||||
self.assertEqual(2, self.test_resource.field_list[1].integer)
|
||||
|
||||
Reference in New Issue
Block a user