diff sat_pubsub/backend.py @ 318:d13526c0eb32

RSM improvments/refactoring: - a warning message is displayed if maxItems == 0 in getItems, and an empty list is returned in this case - use the new container.ItemData instead of doing tuple (un)packing - the list of ItemData => list of domish.Element conversion is done at the end of the workflow - rsm request is checked in self._items_rsm directly - better handling of Response.index in _items_rsm - itemsIdentifiers can't be used with RSM (the later will be ignored if this happen) - don't do approximative unpacking anymore in _items_rsm - countItems and getIndex have been refactored and renamed getItemsCount and getItemsIndex, don't use query duplications anymore - cleaned query handling in getItems - /!\ mam module is temporarly broken
author Goffi <goffi@goffi.org>
date Sun, 03 Jan 2016 18:33:22 +0100
parents 34adc4a8aa64
children 29c2553ef863
line wrap: on
line diff
--- a/sat_pubsub/backend.py	Sun Jan 03 18:33:22 2016 +0100
+++ b/sat_pubsub/backend.py	Sun Jan 03 18:33:22 2016 +0100
@@ -597,28 +597,28 @@
     def _doGetItems(self, result, requestor, maxItems, itemIdentifiers,
                     ext_data):
         node, affiliation = result
+        if maxItems == 0:
+            log.msg("WARNING: maxItems=0 on items retrieval")
+            return []
 
         def append_item_config(items_data):
-            ret = []
+            """Add item config data form to items with roster access model"""
             for item_data in items_data:
-                item, access_model, access_list = item_data.item, item_data.access_model, item_data.config
-                if access_model == const.VAL_AMODEL_OPEN:
+                if item_data.access_model == const.VAL_AMODEL_OPEN:
                     pass
-                elif access_model == const.VAL_AMODEL_ROSTER:
+                elif item_data.access_model == const.VAL_AMODEL_ROSTER:
                     form = data_form.Form('submit', formNamespace=const.NS_ITEM_CONFIG)
                     access = data_form.Field(None, const.OPT_ACCESS_MODEL, value=const.VAL_AMODEL_ROSTER)
-                    allowed = data_form.Field(None, const.OPT_ROSTER_GROUPS_ALLOWED, values=access_list[const.OPT_ROSTER_GROUPS_ALLOWED])
+                    allowed = data_form.Field(None, const.OPT_ROSTER_GROUPS_ALLOWED, values=item_data.config[const.OPT_ROSTER_GROUPS_ALLOWED])
                     form.addField(access)
                     form.addField(allowed)
-                    item.addChild(form.toElement())
+                    item_data.item.addChild(form.toElement())
                 elif access_model == const.VAL_AMODEL_JID:
                     #FIXME: manage jid
                     raise NotImplementedError
                 else:
                     raise error.BadAccessTypeError(access_model)
-
-                ret.append(item)
-            return ret
+            return items_data
 
         def access_checked(access_data):
             authorized, roster = access_data
@@ -627,24 +627,18 @@
 
             roster_item = roster.get(requestor.userhostJID())
             authorized_groups = tuple(roster_item.groups) if roster_item else tuple()
-            unrestricted = affiliation == 'owner'
+            owner = affiliation == 'owner'
 
             if itemIdentifiers:
-                d = node.getItemsById(authorized_groups, unrestricted, itemIdentifiers)
+                d = node.getItemsById(authorized_groups, owner, itemIdentifiers)
             else:
-                d = node.getItems(authorized_groups, unrestricted, maxItems, ext_data)
-                if unrestricted:
+                d = node.getItems(authorized_groups, owner, maxItems, ext_data)
+                if owner:
                     d.addCallback(append_item_config)
 
-            try:
-                rsm_data = ext_data['rsm']
-            except KeyError:
-                pass
-            else:
-                if rsm_data is not None:
-                    d.addCallback(self._items_rsm, node, authorized_groups,
-                                  unrestricted, maxItems, itemIdentifiers,
-                                  rsm_data)
+            d.addCallback(self._items_rsm, node, authorized_groups,
+                          owner, itemIdentifiers,
+                          ext_data)
             return d
 
         if not iidavoll.ILeafNode.providedBy(node):
@@ -671,63 +665,69 @@
             d.addCallback(self.checkGroup, requestor)
             d.addCallback(access_checked)
 
+        d.addCallback(lambda items_data: [item_data.item for item_data in items_data])
+
         return d
 
     def _setCount(self, value, response):
         response.count = value
 
-    def _setIndex(self, value, response):
-        response.index = value
+    def _setIndex(self, value, response, adjust):
+        """Set index in RSM response
+
+        @param value(int): value of the reference index (i.e. before or after item)
+        @param response(RSMResponse): response instance to fill
+        @param adjust(int): adjustement term (i.e. difference between reference index and first item of the result)
+        """
+        response.index = value + adjust
 
-    def _items_rsm(self, elts, node, authorized_groups, unrestricted, maxItems,
-                   itemIdentifiers, request):
-        # FIXME: move this to a separate module ?
+    def _items_rsm(self, items_data, node, authorized_groups, owner,
+                   itemIdentifiers, ext_data):
+        # FIXME: move this to a separate module
+        # TODO: Index can be optimized by keeping a cache of the last RSM request
+        #       An other optimisation would be to look for index first and use it as offset
+        try:
+            rsm_request = ext_data['rsm']
+        except KeyError:
+            # No RSM in this request, nothing to do
+            return items_data
+
+        if itemIdentifiers:
+            log.msg("WARNING, itemIdentifiers used with RSM, ignoring the RSM part")
+            return items_data
+
         response = rsm.RSMResponse()
 
-        d_count = node.countItems(authorized_groups, unrestricted)
+        d_count = node.getItemsCount(authorized_groups, owner, ext_data)
         d_count.addCallback(self._setCount, response)
         d_list = [d_count]
 
-        if request.index is not None:
-            response.index = request.index
-        elif request.before is not None:
-            if request.before != '':
-                # XXX: getIndex starts with index 1, RSM starts with 0
-                d_index = node.getIndex(authorized_groups, unrestricted, request.before)
-                d_index.addCallback(lambda index: max(index - request.max - 1, 0))
-                d_index.addCallback(self._setIndex, response)
+        if items_data:
+            response.first = items_data[0].item['id']
+            response.last = items_data[-1].item['id']
+
+            # index handling
+            if rsm_request.index is not None:
+                response.index = rsm_request.index
+            elif rsm_request.before:
+                # The last page case (before == '') is managed in render method
+                d_index = node.getItemsIndex(rsm_request.before, authorized_groups, owner, ext_data)
+                d_index.addCallback(self._setIndex, response, -len(items_data))
                 d_list.append(d_index)
-        elif request.after is not None:
-            d_index = node.getIndex(authorized_groups, unrestricted, request.after)
-            d_index.addCallback(self._setIndex, response)
-            d_list.append(d_index)
-        elif itemIdentifiers:
-            d_index = node.getIndex(authorized_groups, unrestricted, itemIdentifiers[0])
-            d_index.addCallback(lambda index: index - 1)
-            d_index.addCallback(self._setIndex, response)
-            d_list.append(d_index)
-
+            elif rsm_request.after is not None:
+                d_index = node.getItemsIndex(rsm_request.after, authorized_groups, owner, ext_data)
+                d_index.addCallback(self._setIndex, response, 1)
+                d_list.append(d_index)
+            else:
+                # the first page was requested
+                response.index = 0
 
         def render(result):
-            try:
-                items = [elt for elt in elts if elt.name == 'item']
-            except AttributeError:
-                # XXX: see sat_pubsub.pgsql_storage.LeafNode.getItemsById return value
-                items = [elt[0] for elt in elts if elt[0].name == 'item']
-            if len(items) > 0:
-                if response.index is None:
-                    if request.before == '': # last page
-                        response.index = response.count - request.max
-                    else:  # first page
-                        response.index = 0
-                response.first = items[0]['id']
-                response.last = items[len(items) - 1]['id']
-                if request.before is not None:
-                    response.first, response.last = response.last, response.first
-            else:
-                response.index = None
-            elts.append(response.toElement())
-            return elts
+            if rsm_request.before == '':
+                # the last page was requested
+                response.index = response.count - len(items_data)
+            items_data.append(container.ItemData(response.toElement()))
+            return items_data
 
         return defer.DeferredList(d_list).addCallback(render)