From 5925e50c8c73864d5372b45b53d185a997d7dcc3 Mon Sep 17 00:00:00 2001 From: DJ Johnstone Date: Wed, 23 Jan 2013 11:52:36 -0600 Subject: [PATCH] Correcting the types of the values in the returned data Partially implements: blueprint test-with-xml Change-Id: I6db4ac284b224c994ec6b674cfb8cb053e4b2734 --- reddwarfclient/xml.py | 77 +++++++++++++++++++++++++++++++++++++++---- tests/test_xml.py | 55 +++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/reddwarfclient/xml.py b/reddwarfclient/xml.py index ee064099..82a83457 100644 --- a/reddwarfclient/xml.py +++ b/reddwarfclient/xml.py @@ -5,23 +5,52 @@ from numbers import Number from reddwarfclient import exceptions from reddwarfclient.client import ReddwarfHTTPClient - XML_NS = {None: "http://docs.openstack.org/database/api/v1.0"} -# This dictionary contains XML paths of things that should become list items. +# If XML element is listed here then this searches through the ancestors. LISTIFY = { "accounts": [[]], "databases": [[]], "flavors": [[]], "instances": [[]], - "links": [["flavor", "instance", "instances"], - ["instance", "instances"]], + "links": [[]], "hosts": [[]], "devices": [[]], "users": [[]], "versions": [[]], + "attachments": [[]] } +TYPE_MAP = { + "instance": { + "volume": { + "used": float, + "size": int, + }, + "deleted": bool, + "server": { + "local_id": int, + "deleted": bool, + }, + }, + "instances": { + "deleted": bool, + }, + "deleted": bool, + "flavor": { + "ram": int, + }, + "diagnostics": { + "vmHwm": int, + "vmPeak": int, + "vmSize": int, + "threads": int, + "vmRss": int, + "fdSize": int, + }, +} +TYPE_MAP["flavors"] = TYPE_MAP["flavor"] + REQUEST_AS_LIST = set(['databases', 'users']) @@ -60,8 +89,11 @@ def element_to_json(name, element): def root_element_to_json(name, element): """Returns a tuple of the root JSON value, plus the links if found.""" if name == "rootEnabled": # Why oh why were we inconsistent here? :'( - return bool(element.text), None - elif element_must_be_list(element, name): + if element.text.strip() == "False": + return False, None + elif element.text.strip() == "True": + return True, None + if element_must_be_list(element, name): return element_to_list(element, True) else: return element_to_dict(element), None @@ -93,6 +125,12 @@ def element_to_dict(element): for child_element in element: name = normalize_tag(child_element) result[name] = element_to_json(name, child_element) + if len(result) == 0 and element.text: + string_value = element.text.strip() + if len(string_value): + if string_value == 'None': + return None + return string_value return result @@ -177,10 +215,36 @@ def populate_element_from_dict(element, dict): element.set(key, value) elif isinstance(value, Number): element.set(key, str(value)) + elif isinstance(value, None.__class__): + element.set(key, '') else: create_subelement(element, key, value) +def modify_response_types(value, type_translator): + """ + This will convert some string in response dictionary to ints or bool + so that our respose is compatiable with code expecting JSON style responses + """ + if isinstance(value, str): + if value == 'True': + return True + elif value == 'False': + return False + else: + return type_translator(value) + elif isinstance(value, dict): + for k, v in value.iteritems(): + if v.__class__ is dict and v.__len__() == 0: + value[k] = None + if k in type_translator: + value[k] = modify_response_types(value[k], type_translator[k]) + return value + elif isinstance(value, list): + return [modify_response_types(element, type_translator) + for element in value] + + class ReddwarfXmlClient(ReddwarfHTTPClient): @classmethod @@ -208,4 +272,5 @@ class ReddwarfXmlClient(ReddwarfHTTPClient): result = {root_name: root_value} if links: result['links'] = links + modify_response_types(result, TYPE_MAP) return result diff --git a/tests/test_xml.py b/tests/test_xml.py index 4c20784d..cda382be 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -37,6 +37,20 @@ class XmlTest(TestCase): ['instances', 'instance'])) + def test_populate_element_from_dict(self): + # Test populate_element_from_dict with a None in the data + ele = ''' + + + + + + ''' + rt = etree.fromstring(ele) + + self.assertEqual(None, xml.populate_element_from_dict(rt, + {'size': None})) + def test_element_must_be_list(self): # Test for when name isn't in the dictionary self.assertFalse(xml.element_must_be_list(self.ROOT, "not_in_list")) @@ -66,9 +80,15 @@ class XmlTest(TestCase): self.assertEqual((exp, None), xml.root_element_to_json("not_in_list", self.ROOT)) - # Test rootEnabled: + # Test rootEnabled True: + t_element = etree.fromstring(''' True ''') self.assertEqual((True, None), - xml.root_element_to_json("rootEnabled", self.ROOT)) + xml.root_element_to_json("rootEnabled", t_element)) + + # Test rootEnabled False: + f_element = etree.fromstring(''' False ''') + self.assertEqual((False, None), + xml.root_element_to_json("rootEnabled", f_element)) def test_element_to_list(self): # Test w/ no child elements @@ -89,9 +109,19 @@ class XmlTest(TestCase): check_for_links=True)) def test_element_to_dict(self): + # Test when there is not a None exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}} self.assertEqual(exp, xml.element_to_dict(self.ROOT)) + # Test when there is a None + element = ''' + + None + + ''' + rt = etree.fromstring(element) + self.assertEqual(None, xml.element_to_dict(rt)) + def test_standarize_json(self): xml.standardize_json_lists(self.JSON) self.assertEqual({'instances': ['1', '2', '3'], @@ -154,6 +184,27 @@ class XmlTest(TestCase): except TypeError: pass + def test_modify_response_types(self): + TYPE_MAP = { + "Int": int, + "Bool": bool + } + #Is a string True + self.assertEqual(True, xml.modify_response_types('True', TYPE_MAP)) + + #Is a string False + self.assertEqual(False, xml.modify_response_types('False', TYPE_MAP)) + + #Is a dict + test_dict = {"Int": "5"} + test_dict = xml.modify_response_types(test_dict, TYPE_MAP) + self.assertEqual(int, test_dict["Int"].__class__) + + #Is a list + test_list = {"a_list": [{"Int": "5"}, {"Str": "A"}]} + test_list = xml.modify_response_types(test_list["a_list"], TYPE_MAP) + self.assertEqual([{'Int': 5}, {'Str': 'A'}], test_list) + def test_reddwarfxmlclient(self): from reddwarfclient import exceptions