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
	 DJ Johnstone
					DJ Johnstone