Correcting the types of the values in the returned data
Partially implements: blueprint test-with-xml Change-Id: I6db4ac284b224c994ec6b674cfb8cb053e4b2734
This commit is contained in:
@@ -5,23 +5,52 @@ from numbers import Number
|
|||||||
from reddwarfclient import exceptions
|
from reddwarfclient import exceptions
|
||||||
from reddwarfclient.client import ReddwarfHTTPClient
|
from reddwarfclient.client import ReddwarfHTTPClient
|
||||||
|
|
||||||
|
|
||||||
XML_NS = {None: "http://docs.openstack.org/database/api/v1.0"}
|
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 = {
|
LISTIFY = {
|
||||||
"accounts": [[]],
|
"accounts": [[]],
|
||||||
"databases": [[]],
|
"databases": [[]],
|
||||||
"flavors": [[]],
|
"flavors": [[]],
|
||||||
"instances": [[]],
|
"instances": [[]],
|
||||||
"links": [["flavor", "instance", "instances"],
|
"links": [[]],
|
||||||
["instance", "instances"]],
|
|
||||||
"hosts": [[]],
|
"hosts": [[]],
|
||||||
"devices": [[]],
|
"devices": [[]],
|
||||||
"users": [[]],
|
"users": [[]],
|
||||||
"versions": [[]],
|
"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'])
|
REQUEST_AS_LIST = set(['databases', 'users'])
|
||||||
|
|
||||||
|
|
||||||
@@ -60,8 +89,11 @@ def element_to_json(name, element):
|
|||||||
def root_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."""
|
"""Returns a tuple of the root JSON value, plus the links if found."""
|
||||||
if name == "rootEnabled": # Why oh why were we inconsistent here? :'(
|
if name == "rootEnabled": # Why oh why were we inconsistent here? :'(
|
||||||
return bool(element.text), None
|
if element.text.strip() == "False":
|
||||||
elif element_must_be_list(element, name):
|
return False, None
|
||||||
|
elif element.text.strip() == "True":
|
||||||
|
return True, None
|
||||||
|
if element_must_be_list(element, name):
|
||||||
return element_to_list(element, True)
|
return element_to_list(element, True)
|
||||||
else:
|
else:
|
||||||
return element_to_dict(element), None
|
return element_to_dict(element), None
|
||||||
@@ -93,6 +125,12 @@ def element_to_dict(element):
|
|||||||
for child_element in element:
|
for child_element in element:
|
||||||
name = normalize_tag(child_element)
|
name = normalize_tag(child_element)
|
||||||
result[name] = element_to_json(name, 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
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -177,10 +215,36 @@ def populate_element_from_dict(element, dict):
|
|||||||
element.set(key, value)
|
element.set(key, value)
|
||||||
elif isinstance(value, Number):
|
elif isinstance(value, Number):
|
||||||
element.set(key, str(value))
|
element.set(key, str(value))
|
||||||
|
elif isinstance(value, None.__class__):
|
||||||
|
element.set(key, '')
|
||||||
else:
|
else:
|
||||||
create_subelement(element, key, value)
|
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):
|
class ReddwarfXmlClient(ReddwarfHTTPClient):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -208,4 +272,5 @@ class ReddwarfXmlClient(ReddwarfHTTPClient):
|
|||||||
result = {root_name: root_value}
|
result = {root_name: root_value}
|
||||||
if links:
|
if links:
|
||||||
result['links'] = links
|
result['links'] = links
|
||||||
|
modify_response_types(result, TYPE_MAP)
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -37,6 +37,20 @@ class XmlTest(TestCase):
|
|||||||
['instances',
|
['instances',
|
||||||
'instance']))
|
'instance']))
|
||||||
|
|
||||||
|
def test_populate_element_from_dict(self):
|
||||||
|
# Test populate_element_from_dict with a None in the data
|
||||||
|
ele = '''
|
||||||
|
<instance>
|
||||||
|
<volume>
|
||||||
|
<value size="5"/>
|
||||||
|
</volume>
|
||||||
|
</instance>
|
||||||
|
'''
|
||||||
|
rt = etree.fromstring(ele)
|
||||||
|
|
||||||
|
self.assertEqual(None, xml.populate_element_from_dict(rt,
|
||||||
|
{'size': None}))
|
||||||
|
|
||||||
def test_element_must_be_list(self):
|
def test_element_must_be_list(self):
|
||||||
# Test for when name isn't in the dictionary
|
# Test for when name isn't in the dictionary
|
||||||
self.assertFalse(xml.element_must_be_list(self.ROOT, "not_in_list"))
|
self.assertFalse(xml.element_must_be_list(self.ROOT, "not_in_list"))
|
||||||
@@ -66,9 +80,15 @@ class XmlTest(TestCase):
|
|||||||
self.assertEqual((exp, None),
|
self.assertEqual((exp, None),
|
||||||
xml.root_element_to_json("not_in_list", self.ROOT))
|
xml.root_element_to_json("not_in_list", self.ROOT))
|
||||||
|
|
||||||
# Test rootEnabled:
|
# Test rootEnabled True:
|
||||||
|
t_element = etree.fromstring('''<rootEnabled> True </rootEnabled>''')
|
||||||
self.assertEqual((True, None),
|
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('''<rootEnabled> False </rootEnabled>''')
|
||||||
|
self.assertEqual((False, None),
|
||||||
|
xml.root_element_to_json("rootEnabled", f_element))
|
||||||
|
|
||||||
def test_element_to_list(self):
|
def test_element_to_list(self):
|
||||||
# Test w/ no child elements
|
# Test w/ no child elements
|
||||||
@@ -89,9 +109,19 @@ class XmlTest(TestCase):
|
|||||||
check_for_links=True))
|
check_for_links=True))
|
||||||
|
|
||||||
def test_element_to_dict(self):
|
def test_element_to_dict(self):
|
||||||
|
# Test when there is not a None
|
||||||
exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
|
exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
|
||||||
self.assertEqual(exp, xml.element_to_dict(self.ROOT))
|
self.assertEqual(exp, xml.element_to_dict(self.ROOT))
|
||||||
|
|
||||||
|
# Test when there is a None
|
||||||
|
element = '''
|
||||||
|
<server>
|
||||||
|
None
|
||||||
|
</server>
|
||||||
|
'''
|
||||||
|
rt = etree.fromstring(element)
|
||||||
|
self.assertEqual(None, xml.element_to_dict(rt))
|
||||||
|
|
||||||
def test_standarize_json(self):
|
def test_standarize_json(self):
|
||||||
xml.standardize_json_lists(self.JSON)
|
xml.standardize_json_lists(self.JSON)
|
||||||
self.assertEqual({'instances': ['1', '2', '3'],
|
self.assertEqual({'instances': ['1', '2', '3'],
|
||||||
@@ -154,6 +184,27 @@ class XmlTest(TestCase):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
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):
|
def test_reddwarfxmlclient(self):
|
||||||
from reddwarfclient import exceptions
|
from reddwarfclient import exceptions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user