Merge branch 'develop' into server2server_tls

paul/schema_breaking_changes
Mark Haines 2014-09-01 16:31:09 +01:00
commit 46dcb0d890
8 changed files with 125 additions and 24 deletions

View File

@ -391,22 +391,101 @@ If all members in a room leave, that room becomes eligible for deletion.
Events in a room
----------------
- Split into state and non-state data
- Explain what they are, semantics, give examples of clobbering / not, use cases (msgs vs room names).
Not too much detail on the actual event contents.
- API to hit.
- Extensibility provided by the API for custom events. Examples.
- How this hooks into ``initialSync``.
- See the "Room Events" section for actual spec on each type.
Room events can be split into two categories:
Syncing a room
--------------
- Single room initial sync. ``rooms/<room id>/initialSync`` API to hit. Why it might be used (lazy loading)
:State Events:
These are events which replace events that came before it, depending on a set of unique keys.
These keys are the event ``type`` and a ``state_key``. Events with the same set of keys will
be overwritten. Typically, state events are used to store state, hence their name.
Getting grouped state events
----------------------------
- ``/members`` and ``/messages`` and the events they return.
- ``/state`` and it returns ALL THE THINGS.
:Non-state events:
These are events which cannot be overwritten after sending. The list of events continues
to grow as more events are sent. As this list grows, it becomes necessary to
provide a mechanism for navigating this list. Pagination APIs are used to view the list
of historical non-state events. Typically, non-state events are used to send messages.
This specification outlines several events, all with the event type prefix ``m.``. However,
applications may wish to add their own type of event, and this can be achieved using the
REST API detailed in the following sections. If new events are added, the event ``type``
key SHOULD follow the Java package naming convention, e.g. ``com.example.myapp.event``.
This ensures event types are suitably namespaced for each application and reduces the
risk of clashes.
State events
------------
State events can be sent by ``PUT`` ing to ``/rooms/<room id>/state/<event type>/<state key>``.
These events will be overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all match.
If the state event has no ``state_key``, it can be omitted from the path. These requests
**cannot use transaction IDs** like other ``PUT`` paths because they cannot be differentiated
from the ``state key``. Furthermore, ``POST`` is unsupported on state paths. Valid requests
look like::
PUT /rooms/!roomid:domain/state/m.example.event
{ "key" : "without a state key" }
PUT /rooms/!roomid:domain/state/m.another.example.event/foo
{ "key" : "with 'foo' as the state key" }
In contrast, these requests are invalid::
POST /rooms/!roomid:domain/state/m.example.event/
{ "key" : "cannot use POST here" }
PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
{ "key" : "txnIds are not supported" }
Care should be taken to avoid setting the wrong ``state key``::
PUT /rooms/!roomid:domain/state/m.another.example.event/11
{ "key" : "with '11' as the state key, but was probably intended to be a txnId" }
The ``state_key`` is often used to store state about individual users, by using the user ID as the
value. For example::
PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com
{ "animal" : "cat", "reason": "fluffy" }
In some cases, there may be no need for a ``state_key``, so it can be omitted::
PUT /rooms/!roomid:domain/state/m.room.bgd.color
{ "color": "red", "hex": "#ff0000" }
See "Room Events" for the ``m.`` event specification.
Non-state events
----------------
Non-state events can be sent by sending a request to ``/rooms/<room id>/send/<event type>``.
These requests *can* use transaction IDs and ``PUT``/``POST`` methods. Non-state events
allow access to historical events and pagination, making it best suited for sending messages.
For example::
POST /rooms/!roomid:domain/send/m.custom.example.message
{ "text": "Hello world!" }
PUT /rooms/!roomid:domain/send/m.custom.example.message/11
{ "text": "Goodbye world!" }
See "Room Events" for the ``m.`` event specification.
Syncing rooms
-------------
When a client logs in, they may have a list of rooms which they have already joined. These rooms
may also have a list of events associated with them. The purpose of 'syncing' is to present the
current room and event information in a convenient, compact manner. There are two APIs provided:
- ``/initialSync`` : A global sync which will present room and event information for all rooms
the user has joined.
- ``/rooms/<room id>/initialSync`` : A sync scoped to a single room. Presents room and event
information for this room only.
- TODO: JSON response format for both types
- TODO: when would you use global? when would you use scoped?
Getting grouped state events for a room
---------------------------------------
- ``/members`` and ``/messages`` and the event types they return. Spec JSON response format.
- ``/state`` and it returns ALL THE THINGS.
Room Events
===========

View File

@ -76,6 +76,10 @@ class MessageHandler(BaseRoomHandler):
Raises:
SynapseError if something went wrong.
"""
# TODO(paul): Why does 'event' not have a 'user' object?
user = self.hs.parse_userid(event.user_id)
assert(user.is_mine)
if stamp_event:
event.content["hsob_ts"] = int(self.clock.time_msec())
@ -86,6 +90,10 @@ class MessageHandler(BaseRoomHandler):
yield self._on_new_room_event(event, snapshot)
self.hs.get_handlers().presence_handler.bump_presence_active_time(
user
)
@defer.inlineCallbacks
def get_messages(self, user_id=None, room_id=None, pagin_config=None,
feedback=False):

View File

@ -266,6 +266,12 @@ class PresenceHandler(BaseHandler):
# we don't have to do this all the time
self.changed_presencelike_data(target_user, state)
def bump_presence_active_time(self, user, now=None):
if now is None:
now = self.clock.time_msec()
self.changed_presencelike_data(user, {"last_active": now})
def changed_presencelike_data(self, user, state):
statuscache = self._get_or_make_usercache(user)

View File

@ -51,7 +51,7 @@ class RoomPermissionsTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),
@ -398,7 +398,7 @@ class RoomsMemberListTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),
@ -476,7 +476,7 @@ class RoomsCreateTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),
@ -566,7 +566,7 @@ class RoomTopicTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),
@ -669,7 +669,7 @@ class RoomMemberStateTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),
@ -794,7 +794,7 @@ class RoomMessagesTestCase(RestTestCase):
persistence_service.get_latest_pdus_in_context.return_value = []
hs = HomeServer(
"test",
"red",
db_pool=None,
http_client=None,
datastore=MemoryDataStore(),

View File

@ -188,8 +188,9 @@ class MemoryDataStore(object):
def get_rooms_for_user_where_membership_is(self, user_id, membership_list):
return [
r for r in self.members
if self.members[r].get(user_id).membership in membership_list
self.members[r].get(user_id) for r in self.members
if user_id in self.members[r] and
self.members[r][user_id].membership in membership_list
]
def get_room_events_stream(self, user_id=None, from_key=None, to_key=None,

View File

@ -56,7 +56,7 @@
<div id="footer" ng-hide="location.indexOf('/room') == 0">
<div id="footerContent">
&copy 2014 Matrix.org
&copy; 2014 Matrix.org
</div>
</div>
</body>

View File

@ -43,6 +43,11 @@ angular.module('RecentsController', ['matrixService', 'eventHandlerService'])
$scope.rooms[event.room_id].lastMsg = event;
}
});
$scope.$on(eventHandlerService.CALL_EVENT, function(ngEvent, event, isLive) {
if (isLive) {
$scope.rooms[event.room_id].lastMsg = event;
}
});
};

View File

@ -51,7 +51,9 @@
</div>
<div ng-switch-default>
{{ room.lastMsg }}
<div ng-if="room.lastMsg.type.indexOf('m.call.') == 0">
Call
</div>
</div>
</div>
</td>