diff --git a/sushy/resources/base.py b/sushy/resources/base.py index d3704439..1ad51476 100644 --- a/sushy/resources/base.py +++ b/sushy/resources/base.py @@ -187,6 +187,39 @@ class ListField(Field): return instances +class DictionaryField(Field): + """Base class for fields consisting of dictionary of several sub-fields.""" + + def __init__(self, *args, **kwargs): + super(DictionaryField, self).__init__(*args, **kwargs) + self._subfields = dict(_collect_fields(self)) + + def _load(self, body, resource, nested_in=None): + """Load the dictionary. + + :param body: parent JSON body. + :param resource: parent resource. + :param nested_in: parent resource name (for error reporting only). + :returns: a new dictionary object containing subfields. + """ + nested_in = (nested_in or []) + self._path + values = super(DictionaryField, self)._load(body, resource) + if values is None: + return None + + instances = {} + for key, value in values.items(): + instance_value = copy.copy(self) + for attr, field in self._subfields.items(): + # Hide the Field object behind the real value + setattr(instance_value, attr, field._load(value, + resource, + nested_in)) + instances[key] = instance_value + + 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 8f92ba7d..9509f021 100644 --- a/sushy/tests/unit/resources/test_base.py +++ b/sushy/tests/unit/resources/test_base.py @@ -165,7 +165,11 @@ TEST_JSON = { 'String': 'a fourth string', 'Integer': 2 } - ] + ], + 'Dictionary': { + 'key1': {'property_a': 'value1', 'property_b': 'value2'}, + 'key2': {'property_a': 'value3', 'property_b': 'value4'} + } } @@ -187,11 +191,17 @@ class TestListField(resource_base.ListField): integer = resource_base.Field('Integer', adapter=int) +class TestDictionaryField(resource_base.DictionaryField): + property_a = resource_base.Field('property_a') + property_b = resource_base.Field('property_b') + + 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') + dictionary = TestDictionaryField('Dictionary') non_existing_nested = NestedTestField('NonExistingNested') non_existing_mapped = resource_base.MappedField('NonExistingMapped', MAPPING) @@ -217,6 +227,11 @@ class FieldTestCase(base.TestCase): self.assertEqual('a third string', self.test_resource.field_list[0].string) self.assertEqual(2, self.test_resource.field_list[1].integer) + self.assertEqual(2, len(self.test_resource.dictionary)) + self.assertEqual('value1', + self.test_resource.dictionary['key1'].property_a) + self.assertEqual('value4', + self.test_resource.dictionary['key2'].property_b) self.assertIsNone(self.test_resource.non_existing_nested) self.assertIsNone(self.test_resource.non_existing_mapped)