changeset 317:34adc4a8aa64

new container module, with an ItemData container: this simplify item data manipulation and transmission between storage and backend, it's also better if new data need to be used.
author Goffi <goffi@goffi.org>
date Sun, 03 Jan 2016 18:33:22 +0100
parents cca47e9977a5
children d13526c0eb32
files sat_pubsub/backend.py sat_pubsub/container.py sat_pubsub/pgsql_storage.py
diffstat 3 files changed, 57 insertions(+), 35 deletions(-) [+]
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
@@ -72,18 +72,18 @@
 # from twisted.words.protocols.jabber.jid import JID, InvalidFormat
 from twisted.words.xish import utility
 
-from wokkel import disco, data_form, rsm
-from wokkel.iwokkel import IPubSubResource
-from wokkel.pubsub import PubSubResource, PubSubError, Subscription
+from wokkel import disco
+from wokkel import data_form
+from wokkel import rsm
+from wokkel import iwokkel
+from wokkel import pubsub
 
-from sat_pubsub import error, iidavoll, const
-from sat_pubsub.iidavoll import IBackendService, ILeafNode
+from sat_pubsub import error
+from sat_pubsub import iidavoll
+from sat_pubsub import const
+from sat_pubsub import container
 
 from copy import deepcopy
-from collections import namedtuple
-
-
-ItemData = namedtuple('ItemData', ('item', 'access_model', 'config', 'categories'))
 
 
 def _getAffiliation(node, entity):
@@ -336,7 +336,7 @@
                     check_overwrite = True
             access_model, item_config = self.parseItemConfig(item)
             categories = self.parseCategories(item)
-            items_data.append(ItemData(item, access_model, item_config, categories))
+            items_data.append(container.ItemData(item, access_model, item_config, categories))
 
         if persistItems:
             if check_overwrite and affiliation != 'owner':
@@ -594,11 +594,6 @@
         log.msg("Error while getting roster of {}: {}".format(unicode(owner_jid), failure.value))
         return {}
 
-    def _tuple2ItemData(self, items_data):
-        # FIXME: workaround for the use of ItemData which doesn't exist in storage
-        #        must be done in a cleaner way
-        return [ItemData(item, access_model, access_list, []) for item, access_model, access_list in items_data]
-
     def _doGetItems(self, result, requestor, maxItems, itemIdentifiers,
                     ext_data):
         node, affiliation = result
@@ -636,11 +631,9 @@
 
             if itemIdentifiers:
                 d = node.getItemsById(authorized_groups, unrestricted, itemIdentifiers)
-                d.addCallback(self._tuple2ItemData)
             else:
                 d = node.getItems(authorized_groups, unrestricted, maxItems, ext_data)
                 if unrestricted:
-                    d.addCallback(self._tuple2ItemData)
                     d.addCallback(append_item_config)
 
             try:
@@ -654,7 +647,7 @@
                                   rsm_data)
             return d
 
-        if not ILeafNode.providedBy(node):
+        if not iidavoll.ILeafNode.providedBy(node):
             return []
 
         if affiliation == 'outcast':
@@ -780,7 +773,6 @@
             d = node.getItemsPublishers(itemIdentifiers)
             d.addCallback(checkPublishers)
         d.addCallback(lambda dummy: node.getItemsById(None, True, itemIdentifiers))
-        d.addCallback(self._tuple2ItemData)
         d.addCallback(removeItems)
 
         if notify:
@@ -876,7 +868,7 @@
 
 
 
-class PubSubResourceFromBackend(PubSubResource):
+class PubSubResourceFromBackend(pubsub.PubSubResource):
     """
     Adapts a backend to an xmpp publish-subscribe service.
     """
@@ -932,7 +924,7 @@
     }
 
     def __init__(self, backend):
-        PubSubResource.__init__(self)
+        pubsub.PubSubResource.__init__(self)
 
         self.backend = backend
         self.hideNodes = False
@@ -993,7 +985,7 @@
                 return new_item
 
             notifications_filtered.append((owner_jid,
-                                           set([Subscription(node.nodeIdentifier,
+                                           set([pubsub.Subscription(node.nodeIdentifier,
                                                             owner_jid,
                                                             'subscribed')]),
                                            [getFullItem(item_data) for item_data in items_data]))
@@ -1025,7 +1017,7 @@
             #we add the owner
 
             notifications_filtered.append((owner_jid,
-                                           set([Subscription(node.nodeIdentifier,
+                                           set([pubsub.Subscription(node.nodeIdentifier,
                                                             owner_jid,
                                                             'subscribed')]),
                                            [item_data.item for item_data in items_data]))
@@ -1143,7 +1135,7 @@
         msg = failure.value.msg
 
         if pubsubCondition:
-            exc = PubSubError(condition, pubsubCondition, feature, msg)
+            exc = pubsub.PubSubError(condition, pubsubCondition, feature, msg)
         else:
             exc = StanzaError(condition, text=msg)
 
@@ -1326,5 +1318,5 @@
         return d.addErrback(self._mapErrors)
 
 components.registerAdapter(PubSubResourceFromBackend,
-                           IBackendService,
-                           IPubSubResource)
+                           iidavoll.IBackendService,
+                           iwokkel.IPubSubResource)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sat_pubsub/container.py	Sun Jan 03 18:33:22 2016 +0100
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+#-*- coding: utf-8 -*-
+
+# Copyright (C) 2016 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from collections import namedtuple
+
+
+ItemData = namedtuple('ItemData', ('item', 'access_model', 'config', 'categories'))
+ItemData.__new__.__defaults__ = (None,) * (len(ItemData._fields) - 1) # Only item is mandatory
--- a/sat_pubsub/pgsql_storage.py	Sun Jan 03 18:33:22 2016 +0100
+++ b/sat_pubsub/pgsql_storage.py	Sun Jan 03 18:33:22 2016 +0100
@@ -61,7 +61,10 @@
 from wokkel import generic
 from wokkel.pubsub import Subscription
 
-from sat_pubsub import error, iidavoll, const
+from sat_pubsub import error
+from sat_pubsub import iidavoll
+from sat_pubsub import const
+from sat_pubsub import container
 import psycopg2
 import psycopg2.extensions
 # we wants psycopg2 to return us unicode, not str
@@ -689,7 +692,8 @@
         @param rsm_data: options for RSM feature handling (XEP-0059) as a
                          dictionnary of C{unicode} to C{unicode}.
 
-        @return: list of (item, access_model, id) if unrestricted is True, else list of items
+        @return: list of container.ItemData
+            if unrestricted is False, access_model and config will be None
         """
         if ext_data is None:
             ext_data = {}
@@ -778,10 +782,10 @@
                     cursor.execute('SELECT groupname FROM item_groups_authorized WHERE item_id=%s', (item_id,))
                     access_list[const.OPT_ROSTER_GROUPS_ALLOWED] = [r[0] for r in cursor.fetchall()]
 
-                ret.append((item, access_model, access_list))
+                ret.append(container.ItemData(item, access_model, access_list))
             return ret
-        items = [generic.stripNamespace(parseXml(r[0])) for r in result]
-        return items
+        items_data = [container.ItemData(generic.stripNamespace(parseXml(r[0])), None, None) for r in result]
+        return items_data
 
     def countItems(self, authorized_groups, unrestricted):
         """ Count the accessible items.
@@ -793,6 +797,7 @@
         return self.dbpool.runInteraction(self._countItems, authorized_groups, unrestricted)
 
     def _countItems(self, cursor, authorized_groups, unrestricted):
+        # FIXME: should not be a separate method, but should be an option of getItems instead
         self._checkNodeExists(cursor)
 
         if unrestricted:
@@ -862,8 +867,9 @@
         @param authorized_groups: we want to get items that these groups can access
         @param unrestricted: if true, don't check permissions
         @param itemIdentifiers: list of ids of the items we want to get
-        @return: list of (item, access_model, access_list) if unrestricted is True, else list of items
-            access_list is managed as a dictionnary with same key as for item_config
+        @return: list of container.ItemData
+            ItemData.config will contains access_list (managed as a dictionnary with same key as for item_config)
+            if unrestricted is False, access_model and config will be None
         """
         return self.dbpool.runInteraction(self._getItemsById, authorized_groups, unrestricted, itemIdentifiers)
 
@@ -890,7 +896,7 @@
                     cursor.execute('SELECT groupname FROM item_groups_authorized WHERE item_id=%s', (item_id,))
                     access_list[const.OPT_ROSTER_GROUPS_ALLOWED] = [r[0] for r in cursor.fetchall()]
 
-                ret.append((item, access_model, access_list))
+                ret.append(container.ItemData(item, access_model, access_list))
         else: #we check permission before returning items
             for itemIdentifier in itemIdentifiers:
                 args = [self.nodeDbId, itemIdentifier]
@@ -906,7 +912,7 @@
 
                 result = cursor.fetchone()
                 if result:
-                    ret.append(generic.stripNamespace(parseXml(result[0])))
+                    ret.append(container.ItemData(generic.stripNamespace(parseXml(result[0])), None, None))
 
         return ret