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)
|
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)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class AbstractJsonReader(object):
|
class AbstractJsonReader(object):
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import logging
|
|||||||
|
|
||||||
from sushy.resources import base
|
from sushy.resources import base
|
||||||
from sushy.resources import common
|
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 drive
|
||||||
from sushy.resources.system.storage import volume
|
from sushy.resources.system.storage import volume
|
||||||
from sushy import utils
|
from sushy import utils
|
||||||
@@ -43,12 +44,12 @@ class StorageControllersListField(base.ListField):
|
|||||||
speed_gbps = base.Field('SpeedGbps')
|
speed_gbps = base.Field('SpeedGbps')
|
||||||
"""The maximum speed of the storage controller's device interface."""
|
"""The maximum speed of the storage controller's device interface."""
|
||||||
|
|
||||||
controller_protocols = base.Field('SupportedControllerProtocols',
|
controller_protocols = base.MappedListField(
|
||||||
adapter=list)
|
'SupportedControllerProtocols', res_maps.PROTOCOL_TYPE_VALUE_MAP)
|
||||||
"""The protocols by which this storage controller can be communicated to"""
|
"""The protocols by which this storage controller can be communicated to"""
|
||||||
|
|
||||||
device_protocols = base.Field('SupportedDeviceProtocols',
|
device_protocols = base.MappedListField('SupportedDeviceProtocols',
|
||||||
adapter=list)
|
res_maps.PROTOCOL_TYPE_VALUE_MAP)
|
||||||
"""The protocols which the controller can use tocommunicate with devices"""
|
"""The protocols which the controller can use tocommunicate with devices"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -122,8 +122,10 @@ class StorageTestCase(base.TestCase):
|
|||||||
identifier.durable_name_format)
|
identifier.durable_name_format)
|
||||||
self.assertEqual('345C59DBD970859C', identifier.durable_name)
|
self.assertEqual('345C59DBD970859C', identifier.durable_name)
|
||||||
self.assertEqual(12, controller.speed_gbps)
|
self.assertEqual(12, controller.speed_gbps)
|
||||||
self.assertEqual(["PCIe"], controller.controller_protocols)
|
self.assertEqual([sushy.PROTOCOL_TYPE_PCIe],
|
||||||
self.assertEqual(["SAS", "SATA"], controller.device_protocols)
|
controller.controller_protocols)
|
||||||
|
self.assertEqual([sushy.PROTOCOL_TYPE_SAS, sushy.PROTOCOL_TYPE_SATA],
|
||||||
|
controller.device_protocols)
|
||||||
|
|
||||||
def test_drives_after_refresh(self):
|
def test_drives_after_refresh(self):
|
||||||
self.storage.refresh()
|
self.storage.refresh()
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ class ResourceCollectionBaseTestCase(base.TestCase):
|
|||||||
TEST_JSON = {
|
TEST_JSON = {
|
||||||
'String': 'a string',
|
'String': 'a string',
|
||||||
'Integer': '42',
|
'Integer': '42',
|
||||||
'List': ['a string', 42],
|
'MappedList': ['raw1', 'raw2', 'raw'],
|
||||||
'Nested': {
|
'Nested': {
|
||||||
'String': 'another string',
|
'String': 'another string',
|
||||||
'Integer': 0,
|
'Integer': 0,
|
||||||
@@ -321,7 +321,9 @@ TEST_JSON = {
|
|||||||
|
|
||||||
|
|
||||||
MAPPING = {
|
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)
|
string = resource_base.Field('String', required=True)
|
||||||
integer = resource_base.Field('Integer', adapter=int)
|
integer = resource_base.Field('Integer', adapter=int)
|
||||||
nested = NestedTestField('Nested')
|
nested = NestedTestField('Nested')
|
||||||
|
mapped_list = resource_base.MappedListField('MappedList', MAPPING)
|
||||||
field_list = TestListField('ListField')
|
field_list = TestListField('ListField')
|
||||||
dictionary = TestDictionaryField('Dictionary')
|
dictionary = TestDictionaryField('Dictionary')
|
||||||
non_existing_nested = NestedTestField('NonExistingNested')
|
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('field value', self.test_resource.nested.nested_field)
|
||||||
self.assertEqual('real', self.test_resource.nested.mapped)
|
self.assertEqual('real', self.test_resource.nested.mapped)
|
||||||
self.assertEqual(3.14, self.test_resource.nested.non_existing)
|
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.assertEqual('a third string',
|
||||||
self.test_resource.field_list[0].string)
|
self.test_resource.field_list[0].string)
|
||||||
self.assertEqual(2, self.test_resource.field_list[1].integer)
|
self.assertEqual(2, self.test_resource.field_list[1].integer)
|
||||||
|
|||||||
Reference in New Issue
Block a user