summary refs log tree commit diff
diff options
context:
space:
mode:
authorErik Johnston <erik@matrix.org>2016-01-28 11:52:34 +0000
committerErik Johnston <erik@matrix.org>2016-01-28 11:52:34 +0000
commit4e7948b47a3f197682de82fc0cda07ebb08a581d (patch)
tree24fa68c563cf92df31240df6a5381f960940828b
parentReturn correct type of token (diff)
downloadsynapse-4e7948b47a3f197682de82fc0cda07ebb08a581d.tar.xz
Allow paginating backwards from stream token
-rw-r--r--synapse/handlers/message.py15
-rw-r--r--synapse/storage/stream.py16
-rw-r--r--tests/rest/client/v1/test_rooms.py9
3 files changed, 24 insertions, 16 deletions
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index b73ad62147..82c8cb5f0c 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -16,7 +16,7 @@
 from twisted.internet import defer
 
 from synapse.api.constants import EventTypes, Membership
-from synapse.api.errors import SynapseError, AuthError, Codes
+from synapse.api.errors import AuthError, Codes
 from synapse.streams.config import PaginationConfig
 from synapse.events.utils import serialize_event
 from synapse.events.validator import EventValidator
@@ -119,9 +119,12 @@ class MessageHandler(BaseHandler):
         if source_config.direction == 'b':
             # if we're going backwards, we might need to backfill. This
             # requires that we have a topo token.
-            if room_token.topological is None:
-                raise SynapseError(400, "Invalid token: cannot paginate "
-                                        "backwards from a stream token")
+            if room_token.topological:
+                max_topo = room_token.topological
+            else:
+                max_topo = yield self.store.get_max_topological_token_for_stream_and_room(
+                    room_id, room_token.stream
+                )
 
             if membership == Membership.LEAVE:
                 # If they have left the room then clamp the token to be before
@@ -131,11 +134,11 @@ class MessageHandler(BaseHandler):
                     member_event_id
                 )
                 leave_token = RoomStreamToken.parse(leave_token)
-                if leave_token.topological < room_token.topological:
+                if leave_token.topological < max_topo:
                     source_config.from_key = str(leave_token)
 
             yield self.hs.get_handlers().federation_handler.maybe_backfill(
-                room_id, room_token.topological
+                room_id, max_topo
             )
 
         events, next_key = yield data_source.get_pagination_rows(
diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py
index 28721e6994..5096b46864 100644
--- a/synapse/storage/stream.py
+++ b/synapse/storage/stream.py
@@ -234,10 +234,10 @@ class StreamStore(SQLBaseStore):
                 get_prev_content=True
             )
 
-            ret.reverse()
-
             self._set_before_and_after(ret, rows, topo_order=False)
 
+            ret.reverse()
+
             if rows:
                 key = "s%d" % min(r["stream_ordering"] for r in rows)
             else:
@@ -570,6 +570,18 @@ class StreamStore(SQLBaseStore):
             row["topological_ordering"], row["stream_ordering"],)
         )
 
+    def get_max_topological_token_for_stream_and_room(self, room_id, stream_key):
+        sql = (
+            "SELECT max(topological_ordering) FROM events"
+            " WHERE room_id = ? AND stream_ordering < ?"
+        )
+        return self._execute(
+            "get_max_topological_token_for_stream_and_room", None,
+            sql, room_id, stream_key,
+        ).addCallback(
+            lambda r: r[0][0] if r else 0
+        )
+
     def _get_max_topological_txn(self, txn):
         txn.execute(
             "SELECT MAX(topological_ordering) FROM events"
diff --git a/tests/rest/client/v1/test_rooms.py b/tests/rest/client/v1/test_rooms.py
index 2fe6f695f5..ad5dd3bd6e 100644
--- a/tests/rest/client/v1/test_rooms.py
+++ b/tests/rest/client/v1/test_rooms.py
@@ -1045,13 +1045,6 @@ class RoomMessageListTestCase(RestTestCase):
         self.assertTrue("end" in response)
 
     @defer.inlineCallbacks
-    def test_stream_token_is_rejected_for_back_pagination(self):
-        (code, response) = yield self.mock_resource.trigger_get(
-            "/rooms/%s/messages?access_token=x&from=s0_0_0_0_0&dir=b" %
-            self.room_id)
-        self.assertEquals(400, code)
-
-    @defer.inlineCallbacks
     def test_stream_token_is_accepted_for_fwd_pagianation(self):
         token = "s0_0_0_0_0"
         (code, response) = yield self.mock_resource.trigger_get(
@@ -1061,4 +1054,4 @@ class RoomMessageListTestCase(RestTestCase):
         self.assertTrue("start" in response)
         self.assertEquals(token, response['start'])
         self.assertTrue("chunk" in response)
-        self.assertTrue("end" in response)
\ No newline at end of file
+        self.assertTrue("end" in response)