Merge "Get rid of XML related trove client bindings"
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