From 3220598e39dc5d4019daa7d36aa0aef2ce62e44e Mon Sep 17 00:00:00 2001 From: Nate Potter Date: Fri, 18 Aug 2017 16:10:17 -0700 Subject: [PATCH] Add ListField class to resource base Add a class capable of handling lists of fields in resource body JSON. This is a general purpose class, and is being added for use in the rsd-lib sushy extension as there are JSON responses such as {"Initiator": [{"iSCSI": 1}, {"iSCSI": 2}]} that need to be handled. Change-Id: I6053967177ddb045a79f373e1cf22529d6d71a5c --- sushy/resources/base.py | 34 +++++++++++++++++++++++++ sushy/tests/unit/resources/test_base.py | 21 ++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/sushy/resources/base.py b/sushy/resources/base.py index a6083ba4..31d06e68 100644 --- a/sushy/resources/base.py +++ b/sushy/resources/base.py @@ -153,6 +153,40 @@ class CompositeField(collections.Mapping, Field): return iter(self._subfields) +class ListField(Field): + """Base class for fields consisting of a list of several sub-fields.""" + + def __init__(self, *args, **kwargs): + super(ListField, self).__init__(*args, **kwargs) + self._subfields = dict(_collect_fields(self)) + + def _load(self, body, resource, nested_in=None): + """Load the field 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 subfields. + """ + nested_in = (nested_in or []) + self._path + values = super(ListField, self)._load(body, resource) + if values is None: + return None + + # Initialize the list that will contain each field instance + instances = [] + for value in values: + instance = copy.copy(self) + for attr, field in self._subfields.items(): + # Hide the Field object behind the real value + setattr(instance, attr, field._load(value, + resource, + nested_in)) + instances.append(instance) + + return instances + + class MappedField(Field): """Field taking real value from a mapping.""" diff --git a/sushy/tests/unit/resources/test_base.py b/sushy/tests/unit/resources/test_base.py index 96a1db44..ad7e0ed6 100644 --- a/sushy/tests/unit/resources/test_base.py +++ b/sushy/tests/unit/resources/test_base.py @@ -138,7 +138,17 @@ TEST_JSON = { 'Field': 'field value' }, 'Mapped': 'raw' - } + }, + 'ListField': [ + { + 'String': 'a third string', + 'Integer': 1 + }, + { + 'String': 'a fourth string', + 'Integer': 2 + } + ] } @@ -155,10 +165,16 @@ class NestedTestField(resource_base.CompositeField): non_existing = resource_base.Field('NonExisting', default=3.14) +class TestListField(resource_base.ListField): + string = resource_base.Field('String', required=True) + integer = resource_base.Field('Integer', adapter=int) + + class ComplexResource(resource_base.ResourceBase): string = resource_base.Field('String', required=True) integer = resource_base.Field('Integer', adapter=int) nested = NestedTestField('Nested') + field_list = TestListField('ListField') non_existing_nested = NestedTestField('NonExistingNested') non_existing_mapped = resource_base.MappedField('NonExistingMapped', MAPPING) @@ -181,6 +197,9 @@ 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('a third string', + self.test_resource.field_list[0].string) + self.assertEqual(2, self.test_resource.field_list[1].integer) self.assertIsNone(self.test_resource.non_existing_nested) self.assertIsNone(self.test_resource.non_existing_mapped)