]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add pagination support for xml
authorHe Jie Xu <xuhj@linux.vnet.ibm.com>
Thu, 7 Feb 2013 07:10:29 +0000 (15:10 +0800)
committerHe Jie Xu <xuhj@linux.vnet.ibm.com>
Thu, 21 Feb 2013 07:21:34 +0000 (15:21 +0800)
Fixes bug 1130622

Change-Id: Id9f39daf634906ee222315586fc9a93916160c3f

quantum/common/constants.py
quantum/tests/unit/db/loadbalancer/test_db_loadbalancer.py
quantum/tests/unit/test_db_plugin.py
quantum/wsgi.py

index 1e2c70a2bcb0248b9b5d1d2f0a4659a2dc6fc576..3e27fdf14504e6561a0e134ec719987ead8737dd 100644 (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_ATTR = "xsi:nil"
 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_ATTR = "quantum:type"
 VIRTUAL_ROOT_KEY = "_v_root"
index 26d44ba925bb58933813b2ae655b7076e1cbf3d7..dad18902cd3916bc45a21191c2e154666e0f10af 100644 (file)
@@ -287,8 +287,6 @@ class LoadBalancerPluginDbTestCase(testlib_api.WebTestCase):
 
     def _test_list_with_pagination(self, collection, items, sort,
                                    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_str + ("limit=%s&sort_key=%s&"
                                  "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,
                                            limit, expected_page_num,
                                            query_params=''):
-        if self.fmt == 'xml':
-            self.skipTest("Skip xml test for pagination")
         resources = '%ss' % collection
         collection = collection.replace('-', '_')
         api = self._api_for_resource(resources)
index d78213f5e6e137af4c63cc541240480f39b26f59..3ead154c115d7696739b95b00124227aa06e12bf 100644 (file)
@@ -555,8 +555,6 @@ class QuantumDbPluginV2TestCase(testlib_api.WebTestCase):
     def _test_list_with_pagination(self, collection, items, sort,
                                    limit, expected_page_num, query_params='',
                                    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_str + ("limit=%s&sort_key=%s&"
                                  "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,
                                            limit, expected_page_num,
                                            query_params=''):
-        if self.fmt == 'xml':
-            self.skipTest("Skip xml test for pagination")
         resources = '%ss' % collection
         collection = collection.replace('-', '_')
         api = self._api_for_resource(resources)
index 8e846d06faebcac60f20fdb950865e085a149067..a78c88ffe047be910d72834d85469f52b5a34b68 100644 (file)
@@ -255,21 +255,33 @@ class XMLDictSerializer(DictSerializer):
         self.xmlns = xmlns
 
     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:
-            key_len = data and len(data.keys()) or 0
-            if (key_len == 1):
-                root_key = data.keys()[0]
-                root_value = data[root_key]
-            else:
+            links = None
+            has_atom = False
+            if data is None:
                 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")
             used_prefixes = []
             self._to_xml_node(doc, self.metadata, root_key,
                               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:
             LOG.exception(str(e))
             return ''
@@ -292,7 +304,7 @@ class XMLDictSerializer(DictSerializer):
         node.set('xmlns', self.xmlns)
         node.set(constants.TYPE_XMLNS, self.xmlns)
         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)
         ext_ns = self.metadata.get(constants.EXT_NS, {})
         for prefix in used_prefixes:
@@ -359,6 +371,12 @@ class XMLDictSerializer(DictSerializer):
             result.text = str(data)
         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):
     """Default response headers serialization"""
@@ -462,18 +480,35 @@ class XMLDeserializer(TextDeserializer):
         else:
             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):
         if datastring is None:
             return None
         plurals = set(self.metadata.get('plurals', {}))
         try:
             node = etree.fromstring(datastring)
-            result = self._from_xml_node(node, plurals)
             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:
                 return result
-            else:
-                return {root_tag: result}
+            return dict({root_tag: result}, **links)
         except Exception as e:
             parseError = False
             # Python2.7