Add pagination support for xml

Fixes bug 1130622

Change-Id: Id9f39daf634906ee222315586fc9a93916160c3f
This commit is contained in:
He Jie Xu 2013-02-07 15:10:29 +08:00
parent 2aed847c5e
commit 74f7ef65a9
4 changed files with 51 additions and 21 deletions

View File

@ -42,6 +42,9 @@ XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance" XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
XSI_ATTR = "xsi:nil" XSI_ATTR = "xsi:nil"
XSI_NIL_ATTR = "xmlns:xsi" XSI_NIL_ATTR = "xmlns:xsi"
ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
ATOM_XMLNS = "xmlns:atom"
ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
TYPE_XMLNS = "xmlns:quantum" TYPE_XMLNS = "xmlns:quantum"
TYPE_ATTR = "quantum:type" TYPE_ATTR = "quantum:type"
VIRTUAL_ROOT_KEY = "_v_root" VIRTUAL_ROOT_KEY = "_v_root"

View File

@ -287,8 +287,6 @@ class LoadBalancerPluginDbTestCase(testlib_api.WebTestCase):
def _test_list_with_pagination(self, collection, items, sort, def _test_list_with_pagination(self, collection, items, sort,
limit, expected_page_num, query_params=''): limit, expected_page_num, query_params=''):
if self.fmt == 'xml':
self.skipTest("Skip xml test for pagination")
query_str = query_params + '&' if query_params else '' query_str = query_params + '&' if query_params else ''
query_str = query_str + ("limit=%s&sort_key=%s&" query_str = query_str + ("limit=%s&sort_key=%s&"
"sort_dir=%s") % (limit, sort[0], sort[1]) "sort_dir=%s") % (limit, sort[0], sort[1])
@ -317,8 +315,6 @@ class LoadBalancerPluginDbTestCase(testlib_api.WebTestCase):
def _test_list_with_pagination_reverse(self, collection, items, sort, def _test_list_with_pagination_reverse(self, collection, items, sort,
limit, expected_page_num, limit, expected_page_num,
query_params=''): query_params=''):
if self.fmt == 'xml':
self.skipTest("Skip xml test for pagination")
resources = '%ss' % collection resources = '%ss' % collection
collection = collection.replace('-', '_') collection = collection.replace('-', '_')
api = self._api_for_resource(resources) api = self._api_for_resource(resources)

View File

@ -555,8 +555,6 @@ class QuantumDbPluginV2TestCase(testlib_api.WebTestCase):
def _test_list_with_pagination(self, collection, items, sort, def _test_list_with_pagination(self, collection, items, sort,
limit, expected_page_num, query_params='', limit, expected_page_num, query_params='',
verify_key='id'): verify_key='id'):
if self.fmt == 'xml':
self.skipTest("Skip xml test for pagination")
query_str = query_params + '&' if query_params else '' query_str = query_params + '&' if query_params else ''
query_str = query_str + ("limit=%s&sort_key=%s&" query_str = query_str + ("limit=%s&sort_key=%s&"
"sort_dir=%s") % (limit, sort[0], sort[1]) "sort_dir=%s") % (limit, sort[0], sort[1])
@ -586,8 +584,6 @@ class QuantumDbPluginV2TestCase(testlib_api.WebTestCase):
def _test_list_with_pagination_reverse(self, collection, items, sort, def _test_list_with_pagination_reverse(self, collection, items, sort,
limit, expected_page_num, limit, expected_page_num,
query_params=''): query_params=''):
if self.fmt == 'xml':
self.skipTest("Skip xml test for pagination")
resources = '%ss' % collection resources = '%ss' % collection
collection = collection.replace('-', '_') collection = collection.replace('-', '_')
api = self._api_for_resource(resources) api = self._api_for_resource(resources)

View File

@ -255,21 +255,33 @@ class XMLDictSerializer(DictSerializer):
self.xmlns = xmlns self.xmlns = xmlns
def default(self, data): def default(self, data):
# We expect data to contain a single key which is the XML root or """
# non root :param data: expect data to contain a single key as XML root, or
contain another '*_links' key as atom links. Other
case will use 'VIRTUAL_ROOT_KEY' as XML root.
"""
try: try:
key_len = data and len(data.keys()) or 0 links = None
if (key_len == 1): has_atom = False
root_key = data.keys()[0] if data is None:
root_value = data[root_key]
else:
root_key = constants.VIRTUAL_ROOT_KEY root_key = constants.VIRTUAL_ROOT_KEY
root_value = data root_value = None
else:
link_keys = [k for k in data.iterkeys() or []
if k.endswith('_links')]
if link_keys:
links = data.pop(link_keys[0], None)
has_atom = True
root_key = (len(data) == 1 and
data.keys()[0] or constants.VIRTUAL_ROOT_KEY)
root_value = data.get(root_key, data)
doc = etree.Element("_temp_root") doc = etree.Element("_temp_root")
used_prefixes = [] used_prefixes = []
self._to_xml_node(doc, self.metadata, root_key, self._to_xml_node(doc, self.metadata, root_key,
root_value, used_prefixes) root_value, used_prefixes)
return self.to_xml_string(list(doc)[0], used_prefixes) if links:
self._create_link_nodes(list(doc)[0], links)
return self.to_xml_string(list(doc)[0], used_prefixes, has_atom)
except AttributeError as e: except AttributeError as e:
LOG.exception(str(e)) LOG.exception(str(e))
return '' return ''
@ -292,7 +304,7 @@ class XMLDictSerializer(DictSerializer):
node.set('xmlns', self.xmlns) node.set('xmlns', self.xmlns)
node.set(constants.TYPE_XMLNS, self.xmlns) node.set(constants.TYPE_XMLNS, self.xmlns)
if has_atom: if has_atom:
node.set('xmlns:atom', "http://www.w3.org/2005/Atom") node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE)
node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE) node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE)
ext_ns = self.metadata.get(constants.EXT_NS, {}) ext_ns = self.metadata.get(constants.EXT_NS, {})
for prefix in used_prefixes: for prefix in used_prefixes:
@ -359,6 +371,12 @@ class XMLDictSerializer(DictSerializer):
result.text = str(data) result.text = str(data)
return result return result
def _create_link_nodes(self, xml_doc, links):
for link in links:
link_node = etree.SubElement(xml_doc, 'atom:link')
link_node.set('rel', link['rel'])
link_node.set('href', link['href'])
class ResponseHeaderSerializer(ActionDispatcher): class ResponseHeaderSerializer(ActionDispatcher):
"""Default response headers serialization""" """Default response headers serialization"""
@ -462,18 +480,35 @@ class XMLDeserializer(TextDeserializer):
else: else:
return tag return tag
def _get_links(self, root_tag, node):
link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
root_tag = self._get_key(node.tag)
link_key = "%s_links" % root_tag
link_list = []
for link in link_nodes:
link_list.append({'rel': link.get('rel'),
'href': link.get('href')})
# Remove link node in order to avoid link node process as
# an item in _from_xml_node
node.remove(link)
return link_list and {link_key: link_list} or {}
def _from_xml(self, datastring): def _from_xml(self, datastring):
if datastring is None: if datastring is None:
return None return None
plurals = set(self.metadata.get('plurals', {})) plurals = set(self.metadata.get('plurals', {}))
try: try:
node = etree.fromstring(datastring) node = etree.fromstring(datastring)
result = self._from_xml_node(node, plurals)
root_tag = self._get_key(node.tag) root_tag = self._get_key(node.tag)
# Deserialize link node was needed by unit test for verifying
# the request's response
links = self._get_links(root_tag, node)
result = self._from_xml_node(node, plurals)
# root_tag = constants.VIRTUAL_ROOT_KEY and links is not None
# is not possible because of the way data are serialized.
if root_tag == constants.VIRTUAL_ROOT_KEY: if root_tag == constants.VIRTUAL_ROOT_KEY:
return result return result
else: return dict({root_tag: result}, **links)
return {root_tag: result}
except Exception as e: except Exception as e:
parseError = False parseError = False
# Python2.7 # Python2.7