diff options
| author | Kali Kaneko <kali@leap.se> | 2015-06-05 11:21:17 -0400 | 
|---|---|---|
| committer | Kali Kaneko <kali@leap.se> | 2015-06-15 14:24:20 -0400 | 
| commit | c0c9769f56814fe570c007b7153f60faebea4881 (patch) | |
| tree | 947e11e71e965cc429f5473e5828fc550281106c | |
| parent | 20a76a36f8dce0ebd0fe63690633c0e77727f2c4 (diff) | |
[feature] add post-sync hooks using twisted plugins
implementing a generic plugin interface to allow other modules to react
to soledad syncs, receiving a list of document ids that they've
subscribed to.
- Resolves: #6996
- Releases: 0.7.1
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/adbapi.py | 1 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/api.py | 37 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/interfaces.py | 31 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/sqlcipher.py | 13 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/sync.py | 16 | 
6 files changed, 98 insertions, 1 deletions
@@ -1,3 +1,4 @@ +dropin.cache  *.log  *.pyc  dist/ diff --git a/client/src/leap/soledad/client/adbapi.py b/client/src/leap/soledad/client/adbapi.py index 5b882bbe..4f75695f 100644 --- a/client/src/leap/soledad/client/adbapi.py +++ b/client/src/leap/soledad/client/adbapi.py @@ -217,6 +217,7 @@ class U1DBConnectionPool(adbapi.ConnectionPool):          """          meth = getattr(trans, meth)          return meth(*args, **kw) +        # XXX should return a fetchall?      def _runInteraction(self, interaction, *args, **kw):          """ diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py index 76d6acc3..63b1dfc0 100644 --- a/client/src/leap/soledad/client/api.py +++ b/client/src/leap/soledad/client/api.py @@ -42,6 +42,7 @@ except ImportError:  from StringIO import StringIO  from u1db.remote import http_client  from u1db.remote.ssl_match_hostname import match_hostname +from twisted.plugin import getPlugins  from zope.interface import implements  from leap.common.config import get_path_prefix @@ -70,6 +71,28 @@ Soledad client and server.  """  SOLEDAD_CERT = None +# A whitelist of modules from where to collect plugins dynamically. +# For the moment restricted to leap namespace, but the idea is that we can pass +# other "trusted" modules as options to the initialization of soledad. +PLUGGABLE_LEAP_MODULES = ('mail', 'keymanager') + + +# TODO move to leap.common + +def collect_plugins(interface): +    """ +    Traverse a whitelist of modules and collect all the plugins that implement +    the passed interface. +    """ +    plugins = [] +    for namespace in PLUGGABLE_LEAP_MODULES: +        try: +            module = __import__('leap.%s.plugins' % namespace, fromlist='.') +            plugins = plugins + list(getPlugins(interface, module)) +        except ImportError: +            pass +    return plugins +  class Soledad(object):      """ @@ -656,6 +679,20 @@ class Soledad(object):              defer_decryption=defer_decryption)          def _sync_callback(local_gen): +            self._last_received_docs = self._dbsyncer.received_docs +            print "***" +            print "LAST RECEIVED (API)", self._last_received_docs +            print "***" +            received_doc_ids = self._dbsyncer.received_docs + +            # Post-Sync Hooks +            synced_plugin = soledad_interfaces.ISoledadPostSyncPlugin +            if received_doc_ids: +                suitable_plugins = collect_plugins(synced_plugin) +                for plugin in suitable_plugins: +                    # TODO filter the doc_ids here +                    plugin.process_received_docs(received_doc_ids) +              soledad_events.emit(                  soledad_events.SOLEDAD_DONE_DATA_SYNC, self.uuid)              return local_gen diff --git a/client/src/leap/soledad/client/interfaces.py b/client/src/leap/soledad/client/interfaces.py index 4f7b0779..14b34d24 100644 --- a/client/src/leap/soledad/client/interfaces.py +++ b/client/src/leap/soledad/client/interfaces.py @@ -19,6 +19,37 @@ Interfaces used by the Soledad Client.  """  from zope.interface import Interface, Attribute +# +# Plugins +# + + +class ISoledadPostSyncPlugin(Interface): +    """ +    I implement the minimal methods and attributes for a plugin that can be +    called after a soledad synchronization has ended. +    """ + +    def process_received_docs(self, doc_id_list): +        """ +        Do something with the passed list of doc_ids received after the last +        sync. + +        :param doc_id_list: a list of strings for the received doc_ids +        """ + +    watched_doc_types = Attribute(""" +        a tuple of the watched doc types for this plugin. So far, the +        `doc-types` convention is just the preffix of the doc_id, which is +        basically its first character, followed by a dash. So, for instance, +        `M-` is used for meta-docs in mail, and `F-` is used for flag-docs in +        mail. For now there's no central register of all the doc-types +        used.""") + + +# +# Soledad storage +#  class ILocalStorage(Interface):      """ diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py index b2025130..75d786a6 100644 --- a/client/src/leap/soledad/client/sqlcipher.py +++ b/client/src/leap/soledad/client/sqlcipher.py @@ -456,6 +456,9 @@ class SQLCipherU1DBSync(SQLCipherDatabase):          self._syncers = {} +        # Storage for the documents received during a sync +        self.received_docs = [] +          self.running = False          self._sync_threadpool = None          self._initialize_sync_threadpool() @@ -587,8 +590,16 @@ class SQLCipherU1DBSync(SQLCipherDatabase):          # the following context manager blocks until the syncing lock can be          # acquired.          with self._syncer(url, creds=creds) as syncer: + +            def _record_received_docs(result): +                # beware, closure. syncer is in scope. +                self.received_docs = syncer.received_docs +                return result +              # XXX could mark the critical section here... -            return syncer.sync(defer_decryption=defer_decryption) +            d = syncer.sync(defer_decryption=defer_decryption) +            d.addCallback(_record_received_docs) +            return d      @contextmanager      def _syncer(self, url, creds=None): diff --git a/client/src/leap/soledad/client/sync.py b/client/src/leap/soledad/client/sync.py index 53172f31..917c21ea 100644 --- a/client/src/leap/soledad/client/sync.py +++ b/client/src/leap/soledad/client/sync.py @@ -39,6 +39,7 @@ class SoledadSynchronizer(Synchronizer):      Also modified to allow for interrupting the synchronization process.      """ +    received_docs = []      @defer.inlineCallbacks      def sync(self, defer_decryption=True): @@ -62,6 +63,7 @@ class SoledadSynchronizer(Synchronizer):          :rtype: twisted.internet.defer.Deferred          """          sync_target = self.sync_target +        self.received_docs = []          # get target identifier, its current generation,          # and its last-seen database generation for this source @@ -123,12 +125,14 @@ class SoledadSynchronizer(Synchronizer):          changed_doc_ids = [doc_id for doc_id, _, _ in changes]          docs_to_send = self.source.get_docs(              changed_doc_ids, check_for_conflicts=False, include_deleted=True) +        ids_sent = []          docs_by_generation = []          idx = 0          for doc in docs_to_send:              _, gen, trans = changes[idx]              docs_by_generation.append((doc, gen, trans))              idx += 1 +            ids_sent.append(doc.doc_id)          # exchange documents and try to insert the returned ones with          # the target, return target synced-up-to gen. @@ -151,6 +155,18 @@ class SoledadSynchronizer(Synchronizer):          self._syncing_info = info          yield self.complete_sync() +        _, _, changes = self.source.whats_changed(target_my_gen) +        changed_doc_ids = [doc_id for doc_id, _, _ in changes] + +        print "--------------------------" +        print "SENT", ids_sent +        print "CHANGED_DOC_IDS", changed_doc_ids + +        just_received = list(set(changed_doc_ids) - set(ids_sent)) +        print "RECEIVED:", just_received +        print "--------------------------" + +        self.received_docs = just_received          defer.returnValue(my_gen)      def complete_sync(self):  | 
