Get rid of XML related trove client bindings
We will no longer support XML for the trove API; it behooves us to remove XML related code, and clean up the trove client. Partially implements bp: destroy-xml-api Change-Id: I58f95e81f3e4bc4bad277ff2a48abfced2b48199
This commit is contained in:
		| @@ -5,4 +5,3 @@ testrepository>=0.0.17 | |||||||
| testtools>=0.9.32 | testtools>=0.9.32 | ||||||
| mock>=1.0 | mock>=1.0 | ||||||
| httplib2 | httplib2 | ||||||
| lxml>=2.3 |  | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ import six | |||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from troveclient.compat import client | from troveclient.compat import client | ||||||
| from troveclient.compat import xml |  | ||||||
| from troveclient.compat import exceptions | from troveclient.compat import exceptions | ||||||
|  |  | ||||||
| from troveclient.openstack.common.py3kcompat import urlutils | from troveclient.openstack.common.py3kcompat import urlutils | ||||||
| @@ -102,7 +101,6 @@ class CliOptions(object): | |||||||
|         'verbose': False, |         'verbose': False, | ||||||
|         'debug': False, |         'debug': False, | ||||||
|         'token': None, |         'token': None, | ||||||
|         'xml': None, |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
| @@ -177,12 +175,11 @@ class CliOptions(object): | |||||||
|         add_option("insecure", action="store_true", |         add_option("insecure", action="store_true", | ||||||
|                    help="Run in insecure mode for https endpoints.") |                    help="Run in insecure mode for https endpoints.") | ||||||
|         add_option("token", help="Token from a prior login.") |         add_option("token", help="Token from a prior login.") | ||||||
|         add_option("xml", action="store_true", help="Changes format to XML.") |  | ||||||
|  |  | ||||||
|         oparser.add_option("--secure", action="store_false", dest="insecure", |  | ||||||
|                            help="Run in insecure mode for https endpoints.") |  | ||||||
|         oparser.add_option("--json", action="store_false", dest="xml", |         oparser.add_option("--json", action="store_false", dest="xml", | ||||||
|                            help="Changes format to JSON.") |                            help="Changes format to JSON.") | ||||||
|  |         oparser.add_option("--secure", action="store_false", dest="insecure", | ||||||
|  |                            help="Run in insecure mode for https endpoints.") | ||||||
|         oparser.add_option("--terse", action="store_false", dest="verbose", |         oparser.add_option("--terse", action="store_false", dest="verbose", | ||||||
|                            help="Toggles verbose mode off.") |                            help="Toggles verbose mode off.") | ||||||
|         oparser.add_option("--hide-debug", action="store_false", dest="debug", |         oparser.add_option("--hide-debug", action="store_false", dest="debug", | ||||||
| @@ -218,10 +215,7 @@ class CommandsBase(object): | |||||||
|     def _get_client(self): |     def _get_client(self): | ||||||
|         """Creates the all important client object.""" |         """Creates the all important client object.""" | ||||||
|         try: |         try: | ||||||
|             if self.xml: |             client_cls = client.TroveHTTPClient | ||||||
|                 client_cls = xml.TroveXmlClient |  | ||||||
|             else: |  | ||||||
|                 client_cls = client.TroveHTTPClient |  | ||||||
|             if self.verbose: |             if self.verbose: | ||||||
|                 client.log_to_streamhandler(sys.stdout) |                 client.log_to_streamhandler(sys.stdout) | ||||||
|                 client.RDC_PP = True |                 client.RDC_PP = True | ||||||
|   | |||||||
| @@ -103,7 +103,6 @@ class CliOptionsTest(testtools.TestCase): | |||||||
|         self.assertFalse(co.verbose) |         self.assertFalse(co.verbose) | ||||||
|         self.assertFalse(co.debug) |         self.assertFalse(co.debug) | ||||||
|         self.assertIsNone(co.token) |         self.assertIsNone(co.token) | ||||||
|         self.assertIsNone(co.xml) |  | ||||||
|  |  | ||||||
|     def check_option(self, oparser, option_name): |     def check_option(self, oparser, option_name): | ||||||
|         option = oparser.get_option("--%s" % option_name) |         option = oparser.get_option("--%s" % option_name) | ||||||
| @@ -129,7 +128,7 @@ class CliOptionsTest(testtools.TestCase): | |||||||
|                         "tenant_id", "auth_type", "service_type", |                         "tenant_id", "auth_type", "service_type", | ||||||
|                         "service_name", "service_type", "service_name", |                         "service_name", "service_type", "service_name", | ||||||
|                         "service_url", "region", "insecure", "token", |                         "service_url", "region", "insecure", "token", | ||||||
|                         "xml", "secure", "json", "terse", "hide-debug"] |                         "secure", "json", "terse", "hide-debug"] | ||||||
|  |  | ||||||
|         oparser = common.CliOptions.create_optparser(True) |         oparser = common.CliOptions.create_optparser(True) | ||||||
|         for option_name in option_names: |         for option_name in option_names: | ||||||
|   | |||||||
| @@ -1,257 +0,0 @@ | |||||||
| # Copyright (c) 2011 OpenStack Foundation |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may |  | ||||||
| #    not use this file except in compliance with the License. You may obtain |  | ||||||
| #    a copy of the License at |  | ||||||
| # |  | ||||||
| #         http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| # |  | ||||||
| #    Unless required by applicable law or agreed to in writing, software |  | ||||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |  | ||||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |  | ||||||
| #    License for the specific language governing permissions and limitations |  | ||||||
| #    under the License. |  | ||||||
|  |  | ||||||
| #import testtools |  | ||||||
| from lxml import etree |  | ||||||
| from troveclient.compat import xml |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Killing this until xml support is brought back. |  | ||||||
| #class XmlTest(testtools.TestCase): |  | ||||||
| class XmlTest(object): |  | ||||||
|     ELEMENT = ''' |  | ||||||
|         <instances> |  | ||||||
|             <instance> |  | ||||||
|                 <flavor> |  | ||||||
|                     <links> |  | ||||||
|                     </links> |  | ||||||
|                     <value value="5"/> |  | ||||||
|                 </flavor> |  | ||||||
|             </instance> |  | ||||||
|         </instances> |  | ||||||
|     ''' |  | ||||||
|     ROOT = etree.fromstring(ELEMENT) |  | ||||||
|  |  | ||||||
|     JSON = {'instances': { |  | ||||||
|         'instances': ['1', '2', '3']}, 'dummy': {'dict': True} |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     def test_element_ancestors_match_list(self): |  | ||||||
|         # Test normal operation: |  | ||||||
|         self.assertTrue(xml.element_ancestors_match_list(self.ROOT[0][0], |  | ||||||
|                                                          ['instance', |  | ||||||
|                                                           'instances'])) |  | ||||||
|  |  | ||||||
|         # Test itr_elem is None: |  | ||||||
|         self.assertTrue(xml.element_ancestors_match_list(self.ROOT, |  | ||||||
|                                                          ['instances'])) |  | ||||||
|  |  | ||||||
|         # Test that the first parent element does not match the first list |  | ||||||
|         # element: |  | ||||||
|         self.assertFalse(xml.element_ancestors_match_list(self.ROOT[0][0], |  | ||||||
|                                                           ['instances', |  | ||||||
|                                                            '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.assertIsNone(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")) |  | ||||||
|  |  | ||||||
|         # Test when name is in the dictionary but list is empty |  | ||||||
|         self.assertTrue(xml.element_must_be_list(self.ROOT, "accounts")) |  | ||||||
|  |  | ||||||
|         # Test when name is in the dictionary but list is not empty |  | ||||||
|         self.assertTrue(xml.element_must_be_list(self.ROOT[0][0][0], "links")) |  | ||||||
|  |  | ||||||
|     def test_element_to_json(self): |  | ||||||
|         # Test when element must be list: |  | ||||||
|         self.assertEqual([{'flavor': {'links': [], 'value': {'value': '5'}}}], |  | ||||||
|                          xml.element_to_json("accounts", self.ROOT)) |  | ||||||
|  |  | ||||||
|         # Test when element must not be list: |  | ||||||
|         exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}} |  | ||||||
|         self.assertEqual(exp, xml.element_to_json("not_in_list", self.ROOT)) |  | ||||||
|  |  | ||||||
|     def test_root_element_to_json(self): |  | ||||||
|         # Test when element must be list: |  | ||||||
|         exp = ([{'flavor': {'links': [], 'value': {'value': '5'}}}], None) |  | ||||||
|         self.assertEqual(exp, xml.root_element_to_json("accounts", self.ROOT)) |  | ||||||
|  |  | ||||||
|         # Test when element must not be list: |  | ||||||
|         exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}} |  | ||||||
|         self.assertEqual((exp, None), |  | ||||||
|                          xml.root_element_to_json("not_in_list", self.ROOT)) |  | ||||||
|  |  | ||||||
|         # Test rootEnabled True: |  | ||||||
|         t_element = etree.fromstring('''<rootEnabled> True </rootEnabled>''') |  | ||||||
|         self.assertEqual((True, None), |  | ||||||
|                          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): |  | ||||||
|         # Test w/ no child elements |  | ||||||
|         self.assertEqual([], xml.element_to_list(self.ROOT[0][0][0])) |  | ||||||
|  |  | ||||||
|         # Test w/ no child elements and check_for_links = True |  | ||||||
|         self.assertEqual(([], None), |  | ||||||
|                          xml.element_to_list(self.ROOT[0][0][0], |  | ||||||
|                                              check_for_links=True)) |  | ||||||
|  |  | ||||||
|         # Test w/ child elements |  | ||||||
|         self.assertEqual([{}, {'value': '5'}], |  | ||||||
|                          xml.element_to_list(self.ROOT[0][0])) |  | ||||||
|  |  | ||||||
|         # Test w/ child elements and check_for_links = True |  | ||||||
|         self.assertEqual(([{'value': '5'}], []), |  | ||||||
|                          xml.element_to_list(self.ROOT[0][0], |  | ||||||
|                                              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 = ''' |  | ||||||
|                 <server> |  | ||||||
|                     None |  | ||||||
|                 </server> |  | ||||||
|             ''' |  | ||||||
|         rt = etree.fromstring(element) |  | ||||||
|         self.assertIsNone(xml.element_to_dict(rt)) |  | ||||||
|  |  | ||||||
|     def test_standarize_json(self): |  | ||||||
|         xml.standardize_json_lists(self.JSON) |  | ||||||
|         self.assertEqual({'instances': ['1', '2', '3'], |  | ||||||
|                           'dummy': {'dict': True}}, self.JSON) |  | ||||||
|  |  | ||||||
|     def test_normalize_tag(self): |  | ||||||
|         ELEMENT_NS = ''' |  | ||||||
|             <instances xmlns="http://www.w3.org/1999/xhtml"> |  | ||||||
|                 <instance> |  | ||||||
|                     <flavor> |  | ||||||
|                         <links> |  | ||||||
|                         </links> |  | ||||||
|                         <value value="5"/> |  | ||||||
|                     </flavor> |  | ||||||
|                 </instance> |  | ||||||
|             </instances> |  | ||||||
|         ''' |  | ||||||
|         ROOT_NS = etree.fromstring(ELEMENT_NS) |  | ||||||
|  |  | ||||||
|         # Test normalizing without namespace info |  | ||||||
|         self.assertEqual('instances', xml.normalize_tag(self.ROOT)) |  | ||||||
|  |  | ||||||
|         # Test normalizing with namespace info |  | ||||||
|         self.assertEqual('instances', xml.normalize_tag(ROOT_NS)) |  | ||||||
|  |  | ||||||
|     def test_create_root_xml_element(self): |  | ||||||
|         # Test creating when name is not in REQUEST_AS_LIST |  | ||||||
|         element = xml.create_root_xml_element("root", {"root": "value"}) |  | ||||||
|         exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \ |  | ||||||
|               'root="value"/>' |  | ||||||
|         self.assertEqual(exp, etree.tostring(element)) |  | ||||||
|  |  | ||||||
|         # Test creating when name is in REQUEST_AS_LIST |  | ||||||
|         element = xml.create_root_xml_element("users", []) |  | ||||||
|         exp = '<users xmlns="http://docs.openstack.org/database/api/v1.0"/>' |  | ||||||
|         self.assertEqual(exp, etree.tostring(element)) |  | ||||||
|  |  | ||||||
|     def test_creating_subelements(self): |  | ||||||
|         # Test creating a subelement as a dictionary |  | ||||||
|         element = xml.create_root_xml_element("root", {"root": 5}) |  | ||||||
|         xml.create_subelement(element, "subelement", {"subelement": "value"}) |  | ||||||
|         exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \ |  | ||||||
|               'root="5"><subelement subelement="value"/></root>' |  | ||||||
|         self.assertEqual(exp, etree.tostring(element)) |  | ||||||
|  |  | ||||||
|         # Test creating a subelement as a list |  | ||||||
|         element = xml.create_root_xml_element("root", |  | ||||||
|                                               {"root": {"value": "nested"}}) |  | ||||||
|         xml.create_subelement(element, "subelement", [{"subelement": "value"}]) |  | ||||||
|         exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0">' \ |  | ||||||
|               '<root value="nested"/><subelement><subelement subelement=' \ |  | ||||||
|               '"value"/></subelement></root>' |  | ||||||
|         self.assertEqual(exp, etree.tostring(element)) |  | ||||||
|  |  | ||||||
|         # Test creating a subelement as a string (should raise TypeError) |  | ||||||
|         element = xml.create_root_xml_element("root", {"root": "value"}) |  | ||||||
|         try: |  | ||||||
|             xml.create_subelement(element, "subelement", ["value"]) |  | ||||||
|             self.fail("TypeError exception expected") |  | ||||||
|         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_trovexmlclient(self): |  | ||||||
|         from troveclient import exceptions |  | ||||||
|  |  | ||||||
|         client = xml.TroveXmlClient("user", "password", "tenant", |  | ||||||
|                                     "auth_url", "service_name", |  | ||||||
|                                     auth_strategy="fake") |  | ||||||
|         request = {'headers': {}} |  | ||||||
|  |  | ||||||
|         # Test morph_request, no body |  | ||||||
|         client.morph_request(request) |  | ||||||
|         self.assertEqual('application/xml', request['headers']['Accept']) |  | ||||||
|         self.assertEqual('application/xml', request['headers']['Content-Type']) |  | ||||||
|  |  | ||||||
|         # Test morph_request, with body |  | ||||||
|         request['body'] = {'root': {'test': 'test'}} |  | ||||||
|         client.morph_request(request) |  | ||||||
|         body = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \ |  | ||||||
|                'test="test"/>\n' |  | ||||||
|         exp = {'body': body, |  | ||||||
|                'headers': {'Content-Type': 'application/xml', |  | ||||||
|                            'Accept': 'application/xml'}} |  | ||||||
|         self.assertEqual(exp, request) |  | ||||||
|  |  | ||||||
|         # Test morph_response_body |  | ||||||
|         request = "<users><links><user href='value'/></links></users>" |  | ||||||
|         result = client.morph_response_body(request) |  | ||||||
|         self.assertEqual({'users': [], 'links': [{'href': 'value'}]}, result) |  | ||||||
|  |  | ||||||
|         # Test morph_response_body with improper input |  | ||||||
|         try: |  | ||||||
|             client.morph_response_body("value") |  | ||||||
|             self.fail("ResponseFormatError exception expected") |  | ||||||
|         except exceptions.ResponseFormatError: |  | ||||||
|             pass |  | ||||||
| @@ -1,315 +0,0 @@ | |||||||
| # Copyright (c) 2011 OpenStack Foundation |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may |  | ||||||
| #    not use this file except in compliance with the License. You may obtain |  | ||||||
| #    a copy of the License at |  | ||||||
| # |  | ||||||
| #         http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| # |  | ||||||
| #    Unless required by applicable law or agreed to in writing, software |  | ||||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |  | ||||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |  | ||||||
| #    License for the specific language governing permissions and limitations |  | ||||||
| #    under the License. |  | ||||||
|  |  | ||||||
| from lxml import etree |  | ||||||
| import numbers |  | ||||||
|  |  | ||||||
| from troveclient.compat import exceptions |  | ||||||
| from troveclient.compat import client |  | ||||||
|  |  | ||||||
| XML_NS = {None: "http://docs.openstack.org/database/api/v1.0"} |  | ||||||
|  |  | ||||||
| # If XML element is listed here then this searches through the ancestors. |  | ||||||
| LISTIFY = { |  | ||||||
|     "accounts": [[]], |  | ||||||
|     "databases": [[]], |  | ||||||
|     "flavors": [[]], |  | ||||||
|     "instances": [[]], |  | ||||||
|     "links": [[]], |  | ||||||
|     "hosts": [[]], |  | ||||||
|     "devices": [[]], |  | ||||||
|     "users": [[]], |  | ||||||
|     "versions": [[]], |  | ||||||
|     "attachments": [[]], |  | ||||||
|     "limits": [[]], |  | ||||||
|     "security_groups": [[]], |  | ||||||
|     "backups": [[]], |  | ||||||
|     "datastores": [[]], |  | ||||||
|     "datastore_versions": [[]], |  | ||||||
|     "configuration_parameters": [[]], |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class IntDict(object): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
|  |  | ||||||
| TYPE_MAP = { |  | ||||||
|     "instance": { |  | ||||||
|         "volume": { |  | ||||||
|             "used": float, |  | ||||||
|             "size": int, |  | ||||||
|             "total": float, |  | ||||||
|         }, |  | ||||||
|         "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, |  | ||||||
|     }, |  | ||||||
|     "security_group_rule": { |  | ||||||
|         "from_port": int, |  | ||||||
|         "to_port": int, |  | ||||||
|     }, |  | ||||||
|     "quotas": IntDict, |  | ||||||
|     "configuration_parameter": { |  | ||||||
|         "max": int, |  | ||||||
|         "min": int, |  | ||||||
|     }, |  | ||||||
| } |  | ||||||
| TYPE_MAP["flavors"] = TYPE_MAP["flavor"] |  | ||||||
|  |  | ||||||
| REQUEST_AS_LIST = set(['databases', 'users']) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def element_ancestors_match_list(element, list): |  | ||||||
|     """ |  | ||||||
|     For element root at <foo><blah><root></blah></foo> matches against |  | ||||||
|     list ["blah", "foo"]. |  | ||||||
|     """ |  | ||||||
|     itr_elem = element.getparent() |  | ||||||
|     for name in list: |  | ||||||
|         if itr_elem is None: |  | ||||||
|             break |  | ||||||
|         if name != normalize_tag(itr_elem): |  | ||||||
|             return False |  | ||||||
|         itr_elem = itr_elem.getparent() |  | ||||||
|     return True |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def element_must_be_list(parent_element, name): |  | ||||||
|     """Determines if an element to be created should be a dict or list.""" |  | ||||||
|     if name in LISTIFY: |  | ||||||
|         list_of_lists = LISTIFY[name] |  | ||||||
|         for tag_list in list_of_lists: |  | ||||||
|             if element_ancestors_match_list(parent_element, tag_list): |  | ||||||
|                 return True |  | ||||||
|     return False |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def element_to_json(name, element): |  | ||||||
|     if element_must_be_list(element, name): |  | ||||||
|         return element_to_list(element) |  | ||||||
|     else: |  | ||||||
|         return element_to_dict(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? :'( |  | ||||||
|         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 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def element_to_list(element, check_for_links=False): |  | ||||||
|     """ |  | ||||||
|     For element "foo" in <foos><foo/><foo/></foos> |  | ||||||
|     Returns [{}, {}] |  | ||||||
|     """ |  | ||||||
|     links = None |  | ||||||
|     result = [] |  | ||||||
|     for child_element in element: |  | ||||||
|         # The "links" element gets jammed into the root element. |  | ||||||
|         if check_for_links and normalize_tag(child_element) == "links": |  | ||||||
|             links = element_to_list(child_element) |  | ||||||
|         else: |  | ||||||
|             result.append(element_to_dict(child_element)) |  | ||||||
|     if check_for_links: |  | ||||||
|         return result, links |  | ||||||
|     else: |  | ||||||
|         return result |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def element_to_dict(element): |  | ||||||
|     result = {} |  | ||||||
|     for name, value in element.items(): |  | ||||||
|         result[name] = value |  | ||||||
|     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 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def standardize_json_lists(json_dict): |  | ||||||
|     """ |  | ||||||
|     In XML, we might see something like {'instances':{'instances':[...]}}, |  | ||||||
|     which we must change to just {'instances':[...]} to be compatible with |  | ||||||
|     the true JSON format. |  | ||||||
|  |  | ||||||
|     If any items are dictionaries with only one item which is a list, |  | ||||||
|     simply remove the dictionary and insert its list directly. |  | ||||||
|     """ |  | ||||||
|     found_items = [] |  | ||||||
|     for key, value in json_dict.items(): |  | ||||||
|         value = json_dict[key] |  | ||||||
|         if isinstance(value, dict): |  | ||||||
|             if len(value) == 1 and isinstance(value.values()[0], list): |  | ||||||
|                 found_items.append(key) |  | ||||||
|             else: |  | ||||||
|                 standardize_json_lists(value) |  | ||||||
|     for key in found_items: |  | ||||||
|         json_dict[key] = json_dict[key].values()[0] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def normalize_tag(elem): |  | ||||||
|     """Given an element, returns the tag minus the XMLNS junk. |  | ||||||
|  |  | ||||||
|     IOW, .tag may sometimes return the XML namespace at the start of the |  | ||||||
|     string. This gets rids of that. |  | ||||||
|     """ |  | ||||||
|     try: |  | ||||||
|         prefix = "{" + elem.nsmap[None] + "}" |  | ||||||
|         if elem.tag.startswith(prefix): |  | ||||||
|             return elem.tag[len(prefix):] |  | ||||||
|     except KeyError: |  | ||||||
|         pass |  | ||||||
|     return elem.tag |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_root_xml_element(name, value): |  | ||||||
|     """Create the first element using a name and a dictionary.""" |  | ||||||
|     element = etree.Element(name, nsmap=XML_NS) |  | ||||||
|     if name in REQUEST_AS_LIST: |  | ||||||
|         add_subelements_from_list(element, name, value) |  | ||||||
|     else: |  | ||||||
|         populate_element_from_dict(element, value) |  | ||||||
|     return element |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_subelement(parent_element, name, value): |  | ||||||
|     """Attaches a new element onto the parent element.""" |  | ||||||
|     if isinstance(value, dict): |  | ||||||
|         create_subelement_from_dict(parent_element, name, value) |  | ||||||
|     elif isinstance(value, list): |  | ||||||
|         create_subelement_from_list(parent_element, name, value) |  | ||||||
|     else: |  | ||||||
|         raise TypeError("Can't handle type %s." % type(value)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_subelement_from_dict(parent_element, name, dict): |  | ||||||
|     element = etree.SubElement(parent_element, name) |  | ||||||
|     populate_element_from_dict(element, dict) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_subelement_from_list(parent_element, name, list): |  | ||||||
|     element = etree.SubElement(parent_element, name) |  | ||||||
|     add_subelements_from_list(element, name, list) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def add_subelements_from_list(element, name, list): |  | ||||||
|     if name.endswith("s"): |  | ||||||
|         item_name = name[:len(name) - 1] |  | ||||||
|     else: |  | ||||||
|         item_name = name |  | ||||||
|     for item in list: |  | ||||||
|         create_subelement(element, item_name, item) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def populate_element_from_dict(element, dict): |  | ||||||
|     for key, value in dict.items(): |  | ||||||
|         if isinstance(value, basestring): |  | ||||||
|             element.set(key, value) |  | ||||||
|         elif isinstance(value, numbers.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 response is compatible 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 type_translator is not IntDict: |  | ||||||
|                 if v.__class__ is dict and v.__len__() == 0: |  | ||||||
|                     value[k] = None |  | ||||||
|                 elif k in type_translator: |  | ||||||
|                     value[k] = modify_response_types(value[k], |  | ||||||
|                                                      type_translator[k]) |  | ||||||
|             else: |  | ||||||
|                 value[k] = int(value[k]) |  | ||||||
|         return value |  | ||||||
|     elif isinstance(value, list): |  | ||||||
|         return [modify_response_types(element, type_translator) |  | ||||||
|                 for element in value] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TroveXmlClient(client.TroveHTTPClient): |  | ||||||
|  |  | ||||||
|     @classmethod |  | ||||||
|     def morph_request(self, kwargs): |  | ||||||
|         kwargs['headers']['Accept'] = 'application/xml' |  | ||||||
|         kwargs['headers']['Content-Type'] = 'application/xml' |  | ||||||
|         if 'body' in kwargs: |  | ||||||
|             body = kwargs['body'] |  | ||||||
|             root_name = body.keys()[0] |  | ||||||
|             xml = create_root_xml_element(root_name, body[root_name]) |  | ||||||
|             xml_string = etree.tostring(xml, pretty_print=True) |  | ||||||
|             kwargs['body'] = xml_string |  | ||||||
|  |  | ||||||
|     @classmethod |  | ||||||
|     def morph_response_body(self, body_string): |  | ||||||
|         # The root XML element always becomes a dictionary with a single |  | ||||||
|         # field, which has the same key as the elements name. |  | ||||||
|         result = {} |  | ||||||
|         try: |  | ||||||
|             root_element = etree.XML(body_string) |  | ||||||
|         except etree.XMLSyntaxError: |  | ||||||
|             raise exceptions.ResponseFormatError() |  | ||||||
|         root_name = normalize_tag(root_element) |  | ||||||
|         root_value, links = root_element_to_json(root_name, root_element) |  | ||||||
|         result = {root_name: root_value} |  | ||||||
|         if links: |  | ||||||
|             result['links'] = links |  | ||||||
|         modify_response_types(result, TYPE_MAP) |  | ||||||
|         return result |  | ||||||
| @@ -126,8 +126,6 @@ def _output_override(objs, print_as): | |||||||
|             new_objs = objs |             new_objs = objs | ||||||
|         # pretty print the json |         # pretty print the json | ||||||
|         print(json.dumps(new_objs, indent='  ')) |         print(json.dumps(new_objs, indent='  ')) | ||||||
|     elif 'xml_output' in globals(): |  | ||||||
|         print('not implemented') |  | ||||||
|     else: |     else: | ||||||
|         raise BaseException('No valid output override') |         raise BaseException('No valid output override') | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nikhil Manchanda
					Nikhil Manchanda