Merge pull request #276 from matrix-org/markjh/history_for_rooms_that_have_been_left
SPEC-216: Allow users to view the history of rooms that they have left.pull/278/merge
						commit
						ee2d722f0f
					
				|  | @ -119,6 +119,20 @@ class Auth(object): | |||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def check_joined_room(self, room_id, user_id, current_state=None): | ||||
|         """Check if the user is currently joined in the room | ||||
|         Args: | ||||
|             room_id(str): The room to check. | ||||
|             user_id(str): The user to check. | ||||
|             current_state(dict): Optional map of the current state of the room. | ||||
|                 If provided then that map is used to check whether they are a | ||||
|                 member of the room. Otherwise the current membership is | ||||
|                 loaded from the database. | ||||
|         Raises: | ||||
|             AuthError if the user is not in the room. | ||||
|         Returns: | ||||
|             A deferred membership event for the user if the user is in | ||||
|             the room. | ||||
|         """ | ||||
|         if current_state: | ||||
|             member = current_state.get( | ||||
|                 (EventTypes.Member, user_id), | ||||
|  | @ -134,6 +148,43 @@ class Auth(object): | |||
|         self._check_joined_room(member, user_id, room_id) | ||||
|         defer.returnValue(member) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def check_user_was_in_room(self, room_id, user_id, current_state=None): | ||||
|         """Check if the user was in the room at some point. | ||||
|         Args: | ||||
|             room_id(str): The room to check. | ||||
|             user_id(str): The user to check. | ||||
|             current_state(dict): Optional map of the current state of the room. | ||||
|                 If provided then that map is used to check whether they are a | ||||
|                 member of the room. Otherwise the current membership is | ||||
|                 loaded from the database. | ||||
|         Raises: | ||||
|             AuthError if the user was never in the room. | ||||
|         Returns: | ||||
|             A deferred membership event for the user if the user was in the | ||||
|             room. This will be the join event if they are currently joined to | ||||
|             the room. This will be the leave event if they have left the room. | ||||
|         """ | ||||
|         if current_state: | ||||
|             member = current_state.get( | ||||
|                 (EventTypes.Member, user_id), | ||||
|                 None | ||||
|             ) | ||||
|         else: | ||||
|             member = yield self.state.get_current_state( | ||||
|                 room_id=room_id, | ||||
|                 event_type=EventTypes.Member, | ||||
|                 state_key=user_id | ||||
|             ) | ||||
|         membership = member.membership if member else None | ||||
| 
 | ||||
|         if membership not in (Membership.JOIN, Membership.LEAVE): | ||||
|             raise AuthError(403, "User %s not in room %s" % ( | ||||
|                 user_id, room_id | ||||
|             )) | ||||
| 
 | ||||
|         defer.returnValue(member) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def check_host_in_room(self, room_id, host): | ||||
|         curr_state = yield self.state.get_current_state(room_id) | ||||
|  |  | |||
|  | @ -27,16 +27,6 @@ class Membership(object): | |||
|     LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN) | ||||
| 
 | ||||
| 
 | ||||
| class Feedback(object): | ||||
| 
 | ||||
|     """Represents the types of feedback a user can send in response to a | ||||
|     message.""" | ||||
| 
 | ||||
|     DELIVERED = u"delivered" | ||||
|     READ = u"read" | ||||
|     LIST = (DELIVERED, READ) | ||||
| 
 | ||||
| 
 | ||||
| class PresenceState(object): | ||||
|     """Represents the presence state of a user.""" | ||||
|     OFFLINE = u"offline" | ||||
|  | @ -73,7 +63,6 @@ class EventTypes(object): | |||
|     PowerLevels = "m.room.power_levels" | ||||
|     Aliases = "m.room.aliases" | ||||
|     Redaction = "m.room.redaction" | ||||
|     Feedback = "m.room.message.feedback" | ||||
| 
 | ||||
|     RoomHistoryVisibility = "m.room.history_visibility" | ||||
|     CanonicalAlias = "m.room.canonical_alias" | ||||
|  |  | |||
|  | @ -16,13 +16,13 @@ | |||
| from twisted.internet import defer | ||||
| 
 | ||||
| from synapse.api.constants import EventTypes, Membership | ||||
| from synapse.api.errors import RoomError, SynapseError | ||||
| from synapse.api.errors import SynapseError | ||||
| from synapse.streams.config import PaginationConfig | ||||
| from synapse.events.utils import serialize_event | ||||
| from synapse.events.validator import EventValidator | ||||
| from synapse.util import unwrapFirstError | ||||
| from synapse.util.logcontext import PreserveLoggingContext | ||||
| from synapse.types import UserID, RoomStreamToken | ||||
| from synapse.types import UserID, RoomStreamToken, StreamToken | ||||
| 
 | ||||
| from ._base import BaseHandler | ||||
| 
 | ||||
|  | @ -71,7 +71,7 @@ class MessageHandler(BaseHandler): | |||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_messages(self, user_id=None, room_id=None, pagin_config=None, | ||||
|                      feedback=False, as_client_event=True): | ||||
|                      as_client_event=True): | ||||
|         """Get messages in a room. | ||||
| 
 | ||||
|         Args: | ||||
|  | @ -79,26 +79,52 @@ class MessageHandler(BaseHandler): | |||
|             room_id (str): The room they want messages from. | ||||
|             pagin_config (synapse.api.streams.PaginationConfig): The pagination | ||||
|             config rules to apply, if any. | ||||
|             feedback (bool): True to get compressed feedback with the messages | ||||
|             as_client_event (bool): True to get events in client-server format. | ||||
|         Returns: | ||||
|             dict: Pagination API results | ||||
|         """ | ||||
|         yield self.auth.check_joined_room(room_id, user_id) | ||||
|         member_event = yield self.auth.check_user_was_in_room(room_id, user_id) | ||||
| 
 | ||||
|         data_source = self.hs.get_event_sources().sources["room"] | ||||
| 
 | ||||
|         if not pagin_config.from_token: | ||||
|         if pagin_config.from_token: | ||||
|             room_token = pagin_config.from_token.room_key | ||||
|         else: | ||||
|             pagin_config.from_token = ( | ||||
|                 yield self.hs.get_event_sources().get_current_token( | ||||
|                     direction='b' | ||||
|                 ) | ||||
|             ) | ||||
|             room_token = pagin_config.from_token.room_key | ||||
| 
 | ||||
|         room_token = RoomStreamToken.parse(pagin_config.from_token.room_key) | ||||
|         room_token = RoomStreamToken.parse(room_token) | ||||
|         if room_token.topological is None: | ||||
|             raise SynapseError(400, "Invalid token") | ||||
| 
 | ||||
|         pagin_config.from_token = pagin_config.from_token.copy_and_replace( | ||||
|             "room_key", str(room_token) | ||||
|         ) | ||||
| 
 | ||||
|         source_config = pagin_config.get_source_config("room") | ||||
| 
 | ||||
|         if member_event.membership == Membership.LEAVE: | ||||
|             # If they have left the room then clamp the token to be before | ||||
|             # they left the room | ||||
|             leave_token = yield self.store.get_topological_token_for_event( | ||||
|                 member_event.event_id | ||||
|             ) | ||||
|             leave_token = RoomStreamToken.parse(leave_token) | ||||
|             if leave_token.topological < room_token.topological: | ||||
|                 source_config.from_key = str(leave_token) | ||||
| 
 | ||||
|             if source_config.direction == "f": | ||||
|                 if source_config.to_key is None: | ||||
|                     source_config.to_key = str(leave_token) | ||||
|                 else: | ||||
|                     to_token = RoomStreamToken.parse(source_config.to_key) | ||||
|                     if leave_token.topological < to_token.topological: | ||||
|                         source_config.to_key = str(leave_token) | ||||
| 
 | ||||
|         yield self.hs.get_handlers().federation_handler.maybe_backfill( | ||||
|             room_id, room_token.topological | ||||
|         ) | ||||
|  | @ -106,7 +132,7 @@ class MessageHandler(BaseHandler): | |||
|         user = UserID.from_string(user_id) | ||||
| 
 | ||||
|         events, next_key = yield data_source.get_pagination_rows( | ||||
|             user, pagin_config.get_source_config("room"), room_id | ||||
|             user, source_config, room_id | ||||
|         ) | ||||
| 
 | ||||
|         next_token = pagin_config.from_token.copy_and_replace( | ||||
|  | @ -255,29 +281,26 @@ class MessageHandler(BaseHandler): | |||
|         Raises: | ||||
|             SynapseError if something went wrong. | ||||
|         """ | ||||
|         have_joined = yield self.auth.check_joined_room(room_id, user_id) | ||||
|         if not have_joined: | ||||
|             raise RoomError(403, "User not in room.") | ||||
|         member_event = yield self.auth.check_user_was_in_room(room_id, user_id) | ||||
| 
 | ||||
|         if member_event.membership == Membership.JOIN: | ||||
|             data = yield self.state_handler.get_current_state( | ||||
|                 room_id, event_type, state_key | ||||
|             ) | ||||
|         elif member_event.membership == Membership.LEAVE: | ||||
|             key = (event_type, state_key) | ||||
|             room_state = yield self.store.get_state_for_events( | ||||
|                 room_id, [member_event.event_id], [key] | ||||
|             ) | ||||
|             data = room_state[member_event.event_id].get(key) | ||||
| 
 | ||||
|         data = yield self.state_handler.get_current_state( | ||||
|             room_id, event_type, state_key | ||||
|         ) | ||||
|         defer.returnValue(data) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_feedback(self, event_id): | ||||
|         # yield self.auth.check_joined_room(room_id, user_id) | ||||
| 
 | ||||
|         # Pull out the feedback from the db | ||||
|         fb = yield self.store.get_feedback(event_id) | ||||
| 
 | ||||
|         if fb: | ||||
|             defer.returnValue(fb) | ||||
|         defer.returnValue(None) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_state_events(self, user_id, room_id): | ||||
|         """Retrieve all state events for a given room. | ||||
|         """Retrieve all state events for a given room. If the user is | ||||
|         joined to the room then return the current state. If the user has | ||||
|         left the room return the state events from when they left. | ||||
| 
 | ||||
|         Args: | ||||
|             user_id(str): The user requesting state events. | ||||
|  | @ -285,18 +308,23 @@ class MessageHandler(BaseHandler): | |||
|         Returns: | ||||
|             A list of dicts representing state events. [{}, {}, {}] | ||||
|         """ | ||||
|         yield self.auth.check_joined_room(room_id, user_id) | ||||
|         member_event = yield self.auth.check_user_was_in_room(room_id, user_id) | ||||
| 
 | ||||
|         if member_event.membership == Membership.JOIN: | ||||
|             room_state = yield self.state_handler.get_current_state(room_id) | ||||
|         elif member_event.membership == Membership.LEAVE: | ||||
|             room_state = yield self.store.get_state_for_events( | ||||
|                 room_id, [member_event.event_id], None | ||||
|             ) | ||||
|             room_state = room_state[member_event.event_id] | ||||
| 
 | ||||
|         # TODO: This is duplicating logic from snapshot_all_rooms | ||||
|         current_state = yield self.state_handler.get_current_state(room_id) | ||||
|         now = self.clock.time_msec() | ||||
|         defer.returnValue( | ||||
|             [serialize_event(c, now) for c in current_state.values()] | ||||
|             [serialize_event(c, now) for c in room_state.values()] | ||||
|         ) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def snapshot_all_rooms(self, user_id=None, pagin_config=None, | ||||
|                            feedback=False, as_client_event=True): | ||||
|     def snapshot_all_rooms(self, user_id=None, pagin_config=None, as_client_event=True): | ||||
|         """Retrieve a snapshot of all rooms the user is invited or has joined. | ||||
| 
 | ||||
|         This snapshot may include messages for all rooms where the user is | ||||
|  | @ -306,7 +334,6 @@ class MessageHandler(BaseHandler): | |||
|             user_id (str): The ID of the user making the request. | ||||
|             pagin_config (synapse.api.streams.PaginationConfig): The pagination | ||||
|             config used to determine how many messages *PER ROOM* to return. | ||||
|             feedback (bool): True to get feedback along with these messages. | ||||
|             as_client_event (bool): True to get events in client-server format. | ||||
|         Returns: | ||||
|             A list of dicts with "room_id" and "membership" keys for all rooms | ||||
|  | @ -316,7 +343,9 @@ class MessageHandler(BaseHandler): | |||
|         """ | ||||
|         room_list = yield self.store.get_rooms_for_user_where_membership_is( | ||||
|             user_id=user_id, | ||||
|             membership_list=[Membership.INVITE, Membership.JOIN] | ||||
|             membership_list=[ | ||||
|                 Membership.INVITE, Membership.JOIN, Membership.LEAVE | ||||
|             ] | ||||
|         ) | ||||
| 
 | ||||
|         user = UserID.from_string(user_id) | ||||
|  | @ -358,19 +387,32 @@ class MessageHandler(BaseHandler): | |||
| 
 | ||||
|             rooms_ret.append(d) | ||||
| 
 | ||||
|             if event.membership != Membership.JOIN: | ||||
|             if event.membership not in (Membership.JOIN, Membership.LEAVE): | ||||
|                 return | ||||
| 
 | ||||
|             try: | ||||
|                 if event.membership == Membership.JOIN: | ||||
|                     room_end_token = now_token.room_key | ||||
|                     deferred_room_state = self.state_handler.get_current_state( | ||||
|                         event.room_id | ||||
|                     ) | ||||
|                 elif event.membership == Membership.LEAVE: | ||||
|                     room_end_token = "s%d" % (event.stream_ordering,) | ||||
|                     deferred_room_state = self.store.get_state_for_events( | ||||
|                         event.room_id, [event.event_id], None | ||||
|                     ) | ||||
|                     deferred_room_state.addCallback( | ||||
|                         lambda states: states[event.event_id] | ||||
|                     ) | ||||
| 
 | ||||
|                 (messages, token), current_state = yield defer.gatherResults( | ||||
|                     [ | ||||
|                         self.store.get_recent_events_for_room( | ||||
|                             event.room_id, | ||||
|                             limit=limit, | ||||
|                             end_token=now_token.room_key, | ||||
|                         ), | ||||
|                         self.state_handler.get_current_state( | ||||
|                             event.room_id | ||||
|                             end_token=room_end_token, | ||||
|                         ), | ||||
|                         deferred_room_state, | ||||
|                     ] | ||||
|                 ).addErrback(unwrapFirstError) | ||||
| 
 | ||||
|  | @ -417,15 +459,85 @@ class MessageHandler(BaseHandler): | |||
|         defer.returnValue(ret) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def room_initial_sync(self, user_id, room_id, pagin_config=None, | ||||
|                           feedback=False): | ||||
|         current_state = yield self.state.get_current_state( | ||||
|             room_id=room_id, | ||||
|     def room_initial_sync(self, user_id, room_id, pagin_config=None): | ||||
|         """Capture the a snapshot of a room. If user is currently a member of | ||||
|         the room this will be what is currently in the room. If the user left | ||||
|         the room this will be what was in the room when they left. | ||||
| 
 | ||||
|         Args: | ||||
|             user_id(str): The user to get a snapshot for. | ||||
|             room_id(str): The room to get a snapshot of. | ||||
|             pagin_config(synapse.streams.config.PaginationConfig): | ||||
|                 The pagination config used to determine how many messages to | ||||
|                 return. | ||||
|         Raises: | ||||
|             AuthError if the user wasn't in the room. | ||||
|         Returns: | ||||
|             A JSON serialisable dict with the snapshot of the room. | ||||
|         """ | ||||
| 
 | ||||
|         member_event = yield self.auth.check_user_was_in_room(room_id, user_id) | ||||
| 
 | ||||
|         if member_event.membership == Membership.JOIN: | ||||
|             result = yield self._room_initial_sync_joined( | ||||
|                 user_id, room_id, pagin_config, member_event | ||||
|             ) | ||||
|         elif member_event.membership == Membership.LEAVE: | ||||
|             result = yield self._room_initial_sync_parted( | ||||
|                 user_id, room_id, pagin_config, member_event | ||||
|             ) | ||||
|         defer.returnValue(result) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def _room_initial_sync_parted(self, user_id, room_id, pagin_config, | ||||
|                                   member_event): | ||||
|         room_state = yield self.store.get_state_for_events( | ||||
|             member_event.room_id, [member_event.event_id], None | ||||
|         ) | ||||
| 
 | ||||
|         yield self.auth.check_joined_room( | ||||
|             room_id, user_id, | ||||
|             current_state=current_state | ||||
|         room_state = room_state[member_event.event_id] | ||||
| 
 | ||||
|         limit = pagin_config.limit if pagin_config else None | ||||
|         if limit is None: | ||||
|             limit = 10 | ||||
| 
 | ||||
|         stream_token = yield self.store.get_stream_token_for_event( | ||||
|             member_event.event_id | ||||
|         ) | ||||
| 
 | ||||
|         messages, token = yield self.store.get_recent_events_for_room( | ||||
|             room_id, | ||||
|             limit=limit, | ||||
|             end_token=stream_token | ||||
|         ) | ||||
| 
 | ||||
|         messages = yield self._filter_events_for_client( | ||||
|             user_id, room_id, messages | ||||
|         ) | ||||
| 
 | ||||
|         start_token = StreamToken(token[0], 0, 0, 0) | ||||
|         end_token = StreamToken(token[1], 0, 0, 0) | ||||
| 
 | ||||
|         time_now = self.clock.time_msec() | ||||
| 
 | ||||
|         defer.returnValue({ | ||||
|             "membership": member_event.membership, | ||||
|             "room_id": room_id, | ||||
|             "messages": { | ||||
|                 "chunk": [serialize_event(m, time_now) for m in messages], | ||||
|                 "start": start_token.to_string(), | ||||
|                 "end": end_token.to_string(), | ||||
|             }, | ||||
|             "state": [serialize_event(s, time_now) for s in room_state.values()], | ||||
|             "presence": [], | ||||
|             "receipts": [], | ||||
|         }) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def _room_initial_sync_joined(self, user_id, room_id, pagin_config, | ||||
|                                   member_event): | ||||
|         current_state = yield self.state.get_current_state( | ||||
|             room_id=room_id, | ||||
|         ) | ||||
| 
 | ||||
|         # TODO(paul): I wish I was called with user objects not user_id | ||||
|  | @ -439,8 +551,6 @@ class MessageHandler(BaseHandler): | |||
|             for x in current_state.values() | ||||
|         ] | ||||
| 
 | ||||
|         member_event = current_state.get((EventTypes.Member, user_id,)) | ||||
| 
 | ||||
|         now_token = yield self.hs.get_event_sources().get_current_token() | ||||
| 
 | ||||
|         limit = pagin_config.limit if pagin_config else None | ||||
|  |  | |||
|  | @ -25,7 +25,6 @@ from synapse.api.constants import ( | |||
| from synapse.api.errors import StoreError, SynapseError | ||||
| from synapse.util import stringutils, unwrapFirstError | ||||
| from synapse.util.async import run_on_reactor | ||||
| from synapse.events.utils import serialize_event | ||||
| 
 | ||||
| from collections import OrderedDict | ||||
| import logging | ||||
|  | @ -342,41 +341,6 @@ class RoomMemberHandler(BaseHandler): | |||
|                 if remotedomains is not None: | ||||
|                     remotedomains.add(member.domain) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def get_room_members_as_pagination_chunk(self, room_id=None, user_id=None, | ||||
|                                              limit=0, start_tok=None, | ||||
|                                              end_tok=None): | ||||
|         """Retrieve a list of room members in the room. | ||||
| 
 | ||||
|         Args: | ||||
|             room_id (str): The room to get the member list for. | ||||
|             user_id (str): The ID of the user making the request. | ||||
|             limit (int): The max number of members to return. | ||||
|             start_tok (str): Optional. The start token if known. | ||||
|             end_tok (str): Optional. The end token if known. | ||||
|         Returns: | ||||
|             dict: A Pagination streamable dict. | ||||
|         Raises: | ||||
|             SynapseError if something goes wrong. | ||||
|         """ | ||||
|         yield self.auth.check_joined_room(room_id, user_id) | ||||
| 
 | ||||
|         member_list = yield self.store.get_room_members(room_id=room_id) | ||||
|         time_now = self.clock.time_msec() | ||||
|         event_list = [ | ||||
|             serialize_event(entry, time_now) | ||||
|             for entry in member_list | ||||
|         ] | ||||
|         chunk_data = { | ||||
|             "start": "START",  # FIXME (erikj): START is no longer valid | ||||
|             "end": "END", | ||||
|             "chunk": event_list | ||||
|         } | ||||
|         # TODO honor Pagination stream params | ||||
|         # TODO snapshot this list to return on subsequent requests when | ||||
|         # paginating | ||||
|         defer.returnValue(chunk_data) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def change_membership(self, event, context, do_auth=True): | ||||
|         """ Change the membership status of a user in a room. | ||||
|  | @ -646,7 +610,6 @@ class RoomEventSource(object): | |||
|             to_key=config.to_key, | ||||
|             direction=config.direction, | ||||
|             limit=config.limit, | ||||
|             with_feedback=True | ||||
|         ) | ||||
| 
 | ||||
|         defer.returnValue((events, next_key)) | ||||
|  |  | |||
|  | @ -26,14 +26,12 @@ class InitialSyncRestServlet(ClientV1RestServlet): | |||
|     @defer.inlineCallbacks | ||||
|     def on_GET(self, request): | ||||
|         user, _ = yield self.auth.get_user_by_req(request) | ||||
|         with_feedback = "feedback" in request.args | ||||
|         as_client_event = "raw" not in request.args | ||||
|         pagination_config = PaginationConfig.from_request(request) | ||||
|         handler = self.handlers.message_handler | ||||
|         content = yield handler.snapshot_all_rooms( | ||||
|             user_id=user.to_string(), | ||||
|             pagin_config=pagination_config, | ||||
|             feedback=with_feedback, | ||||
|             as_client_event=as_client_event | ||||
|         ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -290,12 +290,18 @@ class RoomMemberListRestServlet(ClientV1RestServlet): | |||
|     def on_GET(self, request, room_id): | ||||
|         # TODO support Pagination stream API (limit/tokens) | ||||
|         user, _ = yield self.auth.get_user_by_req(request) | ||||
|         handler = self.handlers.room_member_handler | ||||
|         members = yield handler.get_room_members_as_pagination_chunk( | ||||
|         handler = self.handlers.message_handler | ||||
|         events = yield handler.get_state_events( | ||||
|             room_id=room_id, | ||||
|             user_id=user.to_string()) | ||||
|             user_id=user.to_string(), | ||||
|         ) | ||||
| 
 | ||||
|         for event in members["chunk"]: | ||||
|         chunk = [] | ||||
| 
 | ||||
|         for event in events: | ||||
|             if event["type"] != EventTypes.Member: | ||||
|                 continue | ||||
|             chunk.append(event) | ||||
|             # FIXME: should probably be state_key here, not user_id | ||||
|             target_user = UserID.from_string(event["user_id"]) | ||||
|             # Presence is an optional cache; don't fail if we can't fetch it | ||||
|  | @ -308,7 +314,9 @@ class RoomMemberListRestServlet(ClientV1RestServlet): | |||
|             except: | ||||
|                 pass | ||||
| 
 | ||||
|         defer.returnValue((200, members)) | ||||
|         defer.returnValue((200, { | ||||
|             "chunk": chunk | ||||
|         })) | ||||
| 
 | ||||
| 
 | ||||
| # TODO: Needs unit testing | ||||
|  | @ -321,14 +329,12 @@ class RoomMessageListRestServlet(ClientV1RestServlet): | |||
|         pagination_config = PaginationConfig.from_request( | ||||
|             request, default_limit=10, | ||||
|         ) | ||||
|         with_feedback = "feedback" in request.args | ||||
|         as_client_event = "raw" not in request.args | ||||
|         handler = self.handlers.message_handler | ||||
|         msgs = yield handler.get_messages( | ||||
|             room_id=room_id, | ||||
|             user_id=user.to_string(), | ||||
|             pagin_config=pagination_config, | ||||
|             feedback=with_feedback, | ||||
|             as_client_event=as_client_event | ||||
|         ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ logger = logging.getLogger(__name__) | |||
| 
 | ||||
| RoomsForUser = namedtuple( | ||||
|     "RoomsForUser", | ||||
|     ("room_id", "sender", "membership") | ||||
|     ("room_id", "sender", "membership", "event_id", "stream_ordering") | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -141,9 +141,11 @@ class RoomMemberStore(SQLBaseStore): | |||
|         args.extend(membership_list) | ||||
| 
 | ||||
|         sql = ( | ||||
|             "SELECT m.room_id, m.sender, m.membership" | ||||
|             "SELECT m.room_id, m.sender, m.membership, m.event_id, e.stream_ordering" | ||||
|             " FROM room_memberships as m" | ||||
|             " INNER JOIN current_state_events as c" | ||||
|             " ON e.event_id = c.event_id " | ||||
|             " INNER JOIN events as e " | ||||
|             " ON m.event_id = c.event_id " | ||||
|             " AND m.room_id = c.room_id " | ||||
|             " AND m.user_id = c.state_key" | ||||
|  |  | |||
|  | @ -159,9 +159,7 @@ class StreamStore(SQLBaseStore): | |||
| 
 | ||||
|     @log_function | ||||
|     def get_room_events_stream(self, user_id, from_key, to_key, room_id, | ||||
|                                limit=0, with_feedback=False): | ||||
|         # TODO (erikj): Handle compressed feedback | ||||
| 
 | ||||
|                                limit=0): | ||||
|         current_room_membership_sql = ( | ||||
|             "SELECT m.room_id FROM room_memberships as m " | ||||
|             " INNER JOIN current_state_events as c" | ||||
|  | @ -227,10 +225,7 @@ class StreamStore(SQLBaseStore): | |||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def paginate_room_events(self, room_id, from_key, to_key=None, | ||||
|                              direction='b', limit=-1, | ||||
|                              with_feedback=False): | ||||
|         # TODO (erikj): Handle compressed feedback | ||||
| 
 | ||||
|                              direction='b', limit=-1): | ||||
|         # Tokens really represent positions between elements, but we use | ||||
|         # the convention of pointing to the event before the gap. Hence | ||||
|         # we have a bit of asymmetry when it comes to equalities. | ||||
|  | @ -302,7 +297,6 @@ class StreamStore(SQLBaseStore): | |||
| 
 | ||||
|     @cachedInlineCallbacks(num_args=4) | ||||
|     def get_recent_events_for_room(self, room_id, limit, end_token, from_token=None): | ||||
|         # TODO (erikj): Handle compressed feedback | ||||
| 
 | ||||
|         end_token = RoomStreamToken.parse_stream_token(end_token) | ||||
| 
 | ||||
|  | @ -379,6 +373,38 @@ class StreamStore(SQLBaseStore): | |||
|             ) | ||||
|             defer.returnValue("t%d-%d" % (topo, token)) | ||||
| 
 | ||||
|     def get_stream_token_for_event(self, event_id): | ||||
|         """The stream token for an event | ||||
|         Args: | ||||
|             event_id(str): The id of the event to look up a stream token for. | ||||
|         Raises: | ||||
|             StoreError if the event wasn't in the database. | ||||
|         Returns: | ||||
|             A deferred "s%d" stream token. | ||||
|         """ | ||||
|         return self._simple_select_one_onecol( | ||||
|             table="events", | ||||
|             keyvalues={"event_id": event_id}, | ||||
|             retcol="stream_ordering", | ||||
|         ).addCallback(lambda row: "s%d" % (row,)) | ||||
| 
 | ||||
|     def get_topological_token_for_event(self, event_id): | ||||
|         """The stream token for an event | ||||
|         Args: | ||||
|             event_id(str): The id of the event to look up a stream token for. | ||||
|         Raises: | ||||
|             StoreError if the event wasn't in the database. | ||||
|         Returns: | ||||
|             A deferred "t%d-%d" topological token. | ||||
|         """ | ||||
|         return self._simple_select_one( | ||||
|             table="events", | ||||
|             keyvalues={"event_id": event_id}, | ||||
|             retcols=("stream_ordering", "topological_ordering"), | ||||
|         ).addCallback(lambda row: "t%d-%d" % ( | ||||
|             row["topological_ordering"], row["stream_ordering"],) | ||||
|         ) | ||||
| 
 | ||||
|     def _get_max_topological_txn(self, txn): | ||||
|         txn.execute( | ||||
|             "SELECT MAX(topological_ordering) FROM events" | ||||
|  |  | |||
|  | @ -239,7 +239,7 @@ class RoomPermissionsTestCase(RestTestCase): | |||
|                            "PUT", topic_path, topic_content) | ||||
|         self.assertEquals(403, code, msg=str(response)) | ||||
|         (code, response) = yield self.mock_resource.trigger_get(topic_path) | ||||
|         self.assertEquals(403, code, msg=str(response)) | ||||
|         self.assertEquals(200, code, msg=str(response)) | ||||
| 
 | ||||
|         # get topic in PUBLIC room, not joined, expect 403 | ||||
|         (code, response) = yield self.mock_resource.trigger_get( | ||||
|  | @ -301,11 +301,11 @@ class RoomPermissionsTestCase(RestTestCase): | |||
|             room=room, expect_code=200) | ||||
| 
 | ||||
|         # get membership of self, get membership of other, private room + left | ||||
|         # expect all 403s | ||||
|         # expect all 200s | ||||
|         yield self.leave(room=room, user=self.user_id) | ||||
|         yield self._test_get_membership( | ||||
|             members=[self.user_id, self.rmcreator_id], | ||||
|             room=room, expect_code=403) | ||||
|             room=room, expect_code=200) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def test_membership_public_room_perms(self): | ||||
|  | @ -326,11 +326,11 @@ class RoomPermissionsTestCase(RestTestCase): | |||
|             room=room, expect_code=200) | ||||
| 
 | ||||
|         # get membership of self, get membership of other, public room + left | ||||
|         # expect 403. | ||||
|         # expect 200. | ||||
|         yield self.leave(room=room, user=self.user_id) | ||||
|         yield self._test_get_membership( | ||||
|             members=[self.user_id, self.rmcreator_id], | ||||
|             room=room, expect_code=403) | ||||
|             room=room, expect_code=200) | ||||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def test_invited_permissions(self): | ||||
|  | @ -492,9 +492,9 @@ class RoomsMemberListTestCase(RestTestCase): | |||
|         self.assertEquals(200, code, msg=str(response)) | ||||
| 
 | ||||
|         yield self.leave(room=room_id, user=self.user_id) | ||||
|         # can no longer see list, you've left. | ||||
|         # can see old list once left | ||||
|         (code, response) = yield self.mock_resource.trigger_get(room_path) | ||||
|         self.assertEquals(403, code, msg=str(response)) | ||||
|         self.assertEquals(200, code, msg=str(response)) | ||||
| 
 | ||||
| 
 | ||||
| class RoomsCreateTestCase(RestTestCase): | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Mark Haines
						Mark Haines