Merge branch 'develop' of github.com:matrix-org/synapse into stream_refactor

This commit is contained in:
Erik Johnston 2014-08-26 18:57:55 +01:00
commit 93cff1668c
10 changed files with 94 additions and 170 deletions

View File

@ -409,10 +409,9 @@ class SynapseCmd(cmd.Cmd):
def do_send(self, line): def do_send(self, line):
"""Sends a message. "send <roomid> <body>" """ """Sends a message. "send <roomid> <body>" """
args = self._parse(line, ["roomid", "body"]) args = self._parse(line, ["roomid", "body"])
msg_id = "m%s" % int(time.time()) txn_id = "txn%s" % int(time.time())
path = "/rooms/%s/messages/%s/%s" % (urllib.quote(args["roomid"]), path = "/rooms/%s/send/m.room.message/%s" % (urllib.quote(args["roomid"]),
self._usr(), txn_id)
msg_id)
body_json = { body_json = {
"msgtype": "m.text", "msgtype": "m.text",
"body": args["body"] "body": args["body"]

View File

@ -31,8 +31,8 @@ class Feedback(object):
"""Represents the types of feedback a user can send in response to a """Represents the types of feedback a user can send in response to a
message.""" message."""
DELIVERED = u"d" DELIVERED = u"delivered"
READ = u"r" READ = u"read"
LIST = (DELIVERED, READ) LIST = (DELIVERED, READ)

View File

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from synapse.api.constants import Membership from synapse.api.constants import Feedback, Membership
from synapse.api.errors import SynapseError from synapse.api.errors import SynapseError
from . import SynapseEvent from . import SynapseEvent
@ -93,17 +93,19 @@ class MessageEvent(SynapseEvent):
class FeedbackEvent(SynapseEvent): class FeedbackEvent(SynapseEvent):
TYPE = "m.room.message.feedback" TYPE = "m.room.message.feedback"
valid_keys = SynapseEvent.valid_keys + [ valid_keys = SynapseEvent.valid_keys
"msg_id", # the message ID being acknowledged
"msg_sender_id", # person who is sending the feedback is 'user_id'
"feedback_type", # the type of feedback (delivery, read, etc)
]
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(FeedbackEvent, self).__init__(**kwargs) super(FeedbackEvent, self).__init__(**kwargs)
if not kwargs["content"]["type"] in Feedback.LIST:
raise SynapseError(400, "Bad feedback value.")
def get_content_template(self): def get_content_template(self):
return {} return {
"type": u"string",
"target_event_id": u"string",
"msg_sender_id": u"string"
}
class InviteJoinEvent(SynapseEvent): class InviteJoinEvent(SynapseEvent):

View File

@ -19,6 +19,7 @@ from synapse.streams.config import PaginationConfig
from base import RestServlet, client_path_pattern from base import RestServlet, client_path_pattern
# TODO: Needs unit testing
class InitialSyncRestServlet(RestServlet): class InitialSyncRestServlet(RestServlet):
PATTERN = client_path_pattern("/initialSync$") PATTERN = client_path_pattern("/initialSync$")

View File

@ -96,6 +96,7 @@ class RoomCreateRestServlet(RestServlet):
return (200, {}) return (200, {})
# TODO: Needs unit testing for generic events
class RoomStateEventRestServlet(RestServlet): class RoomStateEventRestServlet(RestServlet):
def register(self, http_server): def register(self, http_server):
# /room/$roomid/state/$eventtype # /room/$roomid/state/$eventtype
@ -168,6 +169,47 @@ class RoomStateEventRestServlet(RestServlet):
defer.returnValue((200, "")) defer.returnValue((200, ""))
# TODO: Needs unit testing for generic events + feedback
class RoomSendEventRestServlet(RestServlet):
def register(self, http_server):
# /rooms/$roomid/send/$event_type[/$txn_id]
PATTERN = ("/rooms/(?P<room_id>[^/]*)/send/(?P<event_type>[^/]*)")
register_txn_path(self, PATTERN, http_server, with_get=True)
@defer.inlineCallbacks
def on_POST(self, request, room_id, event_type):
user = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
event = self.event_factory.create_event(
etype=event_type,
room_id=urllib.unquote(room_id),
user_id=user.to_string(),
content=content
)
msg_handler = self.handlers.message_handler
yield msg_handler.send_message(event)
defer.returnValue((200, {"event_id": event.event_id}))
def on_GET(self, request, room_id, event_type, txn_id):
return (200, "Not implemented")
@defer.inlineCallbacks
def on_PUT(self, request, room_id, event_type, txn_id):
try:
defer.returnValue(self.txns.get_client_transaction(request, txn_id))
except KeyError:
pass
response = yield self.on_POST(request, room_id, event_type)
self.txns.store_client_transaction(request, txn_id, response)
defer.returnValue(response)
class JoinRoomAliasServlet(RestServlet): class JoinRoomAliasServlet(RestServlet):
PATTERN = client_path_pattern("/join/(?P<room_alias>[^/]+)$") PATTERN = client_path_pattern("/join/(?P<room_alias>[^/]+)$")
@ -188,123 +230,7 @@ class JoinRoomAliasServlet(RestServlet):
defer.returnValue((200, ret_dict)) defer.returnValue((200, ret_dict))
class MessageRestServlet(RestServlet): # TODO: Needs unit testing
PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/messages/"
+ "(?P<sender_id>[^/]*)/(?P<msg_id>[^/]*)$")
def get_event_type(self):
return MessageEvent.TYPE
@defer.inlineCallbacks
def on_GET(self, request, room_id, sender_id, msg_id):
user = yield self.auth.get_user_by_req(request)
msg_handler = self.handlers.message_handler
msg = yield msg_handler.get_message(room_id=urllib.unquote(room_id),
sender_id=urllib.unquote(sender_id),
msg_id=msg_id,
user_id=user.to_string(),
)
if not msg:
raise SynapseError(404, "Message not found.",
errcode=Codes.NOT_FOUND)
defer.returnValue((200, json.loads(msg.content)))
@defer.inlineCallbacks
def on_PUT(self, request, room_id, sender_id, msg_id):
user = yield self.auth.get_user_by_req(request)
if user.to_string() != urllib.unquote(sender_id):
raise SynapseError(403, "Must send messages as yourself.",
errcode=Codes.FORBIDDEN)
content = _parse_json(request)
event = self.event_factory.create_event(
etype=self.get_event_type(),
room_id=urllib.unquote(room_id),
user_id=user.to_string(),
msg_id=msg_id,
content=content
)
msg_handler = self.handlers.message_handler
yield msg_handler.send_message(event)
defer.returnValue((200, ""))
class FeedbackRestServlet(RestServlet):
PATTERN = client_path_pattern(
"/rooms/(?P<room_id>[^/]*)/messages/" +
"(?P<msg_sender_id>[^/]*)/(?P<msg_id>[^/]*)/feedback/" +
"(?P<sender_id>[^/]*)/(?P<feedback_type>[^/]*)$"
)
def get_event_type(self):
return FeedbackEvent.TYPE
@defer.inlineCallbacks
def on_GET(self, request, room_id, msg_sender_id, msg_id, fb_sender_id,
feedback_type):
yield (self.auth.get_user_by_req(request))
# TODO (erikj): Implement this?
raise NotImplementedError("Getting feedback is not supported")
# if feedback_type not in Feedback.LIST:
# raise SynapseError(400, "Bad feedback type.",
# errcode=Codes.BAD_JSON)
#
# msg_handler = self.handlers.message_handler
# feedback = yield msg_handler.get_feedback(
# room_id=urllib.unquote(room_id),
# msg_sender_id=msg_sender_id,
# msg_id=msg_id,
# user_id=user.to_string(),
# fb_sender_id=fb_sender_id,
# fb_type=feedback_type
# )
#
# if not feedback:
# raise SynapseError(404, "Feedback not found.",
# errcode=Codes.NOT_FOUND)
#
# defer.returnValue((200, json.loads(feedback.content)))
@defer.inlineCallbacks
def on_PUT(self, request, room_id, sender_id, msg_id, fb_sender_id,
feedback_type):
user = yield (self.auth.get_user_by_req(request))
if user.to_string() != fb_sender_id:
raise SynapseError(403, "Must send feedback as yourself.",
errcode=Codes.FORBIDDEN)
if feedback_type not in Feedback.LIST:
raise SynapseError(400, "Bad feedback type.",
errcode=Codes.BAD_JSON)
content = _parse_json(request)
event = self.event_factory.create_event(
etype=self.get_event_type(),
room_id=urllib.unquote(room_id),
msg_sender_id=sender_id,
msg_id=msg_id,
user_id=user.to_string(), # user sending the feedback
feedback_type=feedback_type,
content=content
)
msg_handler = self.handlers.message_handler
yield msg_handler.send_feedback(event)
defer.returnValue((200, ""))
class RoomMemberListRestServlet(RestServlet): class RoomMemberListRestServlet(RestServlet):
PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/members$") PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/members$")
@ -332,6 +258,7 @@ class RoomMemberListRestServlet(RestServlet):
defer.returnValue((200, members)) defer.returnValue((200, members))
# TODO: Needs unit testing
class RoomMessageListRestServlet(RestServlet): class RoomMessageListRestServlet(RestServlet):
PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/messages$") PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/messages$")
@ -366,6 +293,7 @@ class RoomTriggerBackfill(RestServlet):
defer.returnValue((200, res)) defer.returnValue((200, res))
# TODO: Needs unit testing
class RoomMembershipRestServlet(RestServlet): class RoomMembershipRestServlet(RestServlet):
def register(self, http_server): def register(self, http_server):
@ -402,7 +330,7 @@ class RoomMembershipRestServlet(RestServlet):
def on_PUT(self, request, room_id, membership_action, txn_id): def on_PUT(self, request, room_id, membership_action, txn_id):
try: try:
defer.returnValue(self.txns.get_client_transaction(request, txn_id)) defer.returnValue(self.txns.get_client_transaction(request, txn_id))
except: except KeyError:
pass pass
response = yield self.on_POST(request, room_id, membership_action) response = yield self.on_POST(request, room_id, membership_action)
@ -422,7 +350,7 @@ def _parse_json(request):
raise SynapseError(400, "Content not JSON.", errcode=Codes.NOT_JSON) raise SynapseError(400, "Content not JSON.", errcode=Codes.NOT_JSON)
def register_txn_path(servlet, regex_string, http_server): def register_txn_path(servlet, regex_string, http_server, with_get=False):
"""Registers a transaction-based path. """Registers a transaction-based path.
This registers two paths: This registers two paths:
@ -433,6 +361,7 @@ def register_txn_path(servlet, regex_string, http_server):
regex_string (str): The regex string to register. Must NOT have a regex_string (str): The regex string to register. Must NOT have a
trailing $ as this string will be appended to. trailing $ as this string will be appended to.
http_server : The http_server to register paths with. http_server : The http_server to register paths with.
with_get: True to also register respective GET paths for the PUTs.
""" """
http_server.register_path( http_server.register_path(
"POST", "POST",
@ -444,15 +373,20 @@ def register_txn_path(servlet, regex_string, http_server):
client_path_pattern(regex_string + "/(?P<txn_id>[^/]*)$"), client_path_pattern(regex_string + "/(?P<txn_id>[^/]*)$"),
servlet.on_PUT servlet.on_PUT
) )
if with_get:
http_server.register_path(
"GET",
client_path_pattern(regex_string + "/(?P<txn_id>[^/]*)$"),
servlet.on_GET
)
def register_servlets(hs, http_server): def register_servlets(hs, http_server):
RoomStateEventRestServlet(hs).register(http_server) RoomStateEventRestServlet(hs).register(http_server)
MessageRestServlet(hs).register(http_server)
FeedbackRestServlet(hs).register(http_server)
RoomCreateRestServlet(hs).register(http_server) RoomCreateRestServlet(hs).register(http_server)
RoomMemberListRestServlet(hs).register(http_server) RoomMemberListRestServlet(hs).register(http_server)
RoomMessageListRestServlet(hs).register(http_server) RoomMessageListRestServlet(hs).register(http_server)
JoinRoomAliasServlet(hs).register(http_server) JoinRoomAliasServlet(hs).register(http_server)
RoomTriggerBackfill(hs).register(http_server) RoomTriggerBackfill(hs).register(http_server)
RoomMembershipRestServlet(hs).register(http_server) RoomMembershipRestServlet(hs).register(http_server)
RoomSendEventRestServlet(hs).register(http_server)

View File

@ -23,9 +23,9 @@ class FeedbackStore(SQLBaseStore):
def _store_feedback(self, event): def _store_feedback(self, event):
return self._simple_insert("feedback", { return self._simple_insert("feedback", {
"event_id": event.event_id, "event_id": event.event_id,
"feedback_type": event.feedback_type, "feedback_type": event.content["type"],
"room_id": event.room_id, "room_id": event.room_id,
"target_event_id": event.target_event, "target_event_id": event.content["target_event_id"],
"sender": event.user_id, "sender": event.user_id,
}) })

View File

@ -181,7 +181,7 @@ class EventStreamPermissionsTestCase(RestTestCase):
room_id = "!rid1:test" room_id = "!rid1:test"
yield self.create_room_as(room_id, self.other_user, yield self.create_room_as(room_id, self.other_user,
tok=self.other_token) tok=self.other_token)
yield self.send(room_id, self.other_user, tok=self.other_token) yield self.send(room_id, tok=self.other_token)
# invited to room (expect no content for room) # invited to room (expect no content for room)
yield self.invite(room_id, src=self.other_user, targ=self.user_id, yield self.invite(room_id, src=self.other_user, targ=self.user_id,

View File

@ -83,8 +83,8 @@ class RoomPermissionsTestCase(RestTestCase):
is_public=True) is_public=True)
# send a message in one of the rooms # send a message in one of the rooms
self.created_rmid_msg_path = ("/rooms/%s/messages/%s/midaaa1" % self.created_rmid_msg_path = ("/rooms/%s/send/m.room.message/a1" %
(self.created_rmid, self.rmcreator_id)) (self.created_rmid))
(code, response) = yield self.mock_resource.trigger( (code, response) = yield self.mock_resource.trigger(
"PUT", "PUT",
self.created_rmid_msg_path, self.created_rmid_msg_path,
@ -138,14 +138,14 @@ class RoomPermissionsTestCase(RestTestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_send_message(self): def test_send_message(self):
msg_content = '{"msgtype":"m.text","body":"hello"}' msg_content = '{"msgtype":"m.text","body":"hello"}'
send_msg_path = ("/rooms/%s/messages/%s/mid1" % send_msg_path = ("/rooms/%s/send/m.room.message/mid1" %
(self.created_rmid, self.user_id)) (self.created_rmid))
# send message in uncreated room, expect 403 # send message in uncreated room, expect 403
(code, response) = yield self.mock_resource.trigger( (code, response) = yield self.mock_resource.trigger(
"PUT", "PUT",
"/rooms/%s/messages/%s/mid1" % "/rooms/%s/send/m.room.message/mid2" %
(self.uncreated_rmid, self.user_id), msg_content) (self.uncreated_rmid), msg_content)
self.assertEquals(403, code, msg=str(response)) self.assertEquals(403, code, msg=str(response))
# send message in created room not joined (no state), expect 403 # send message in created room not joined (no state), expect 403
@ -875,9 +875,8 @@ class RoomMessagesTestCase(RestTestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_invalid_puts(self): def test_invalid_puts(self):
path = "/rooms/%s/messages/%s/mid1" % ( path = "/rooms/%s/send/m.room.message/mid1" % (
urllib.quote(self.room_id), self.user_id urllib.quote(self.room_id))
)
# missing keys or invalid json # missing keys or invalid json
(code, response) = yield self.mock_resource.trigger("PUT", (code, response) = yield self.mock_resource.trigger("PUT",
path, '{}') path, '{}')
@ -905,9 +904,8 @@ class RoomMessagesTestCase(RestTestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_rooms_messages_sent(self): def test_rooms_messages_sent(self):
path = "/rooms/%s/messages/%s/mid1" % ( path = "/rooms/%s/send/m.room.message/mid1" % (
urllib.quote(self.room_id), self.user_id urllib.quote(self.room_id))
)
content = '{"body":"test","msgtype":{"type":"a"}}' content = '{"body":"test","msgtype":{"type":"a"}}'
(code, response) = yield self.mock_resource.trigger("PUT", path, content) (code, response) = yield self.mock_resource.trigger("PUT", path, content)
@ -923,9 +921,8 @@ class RoomMessagesTestCase(RestTestCase):
# self.assert_dict(json.loads(content), response) # self.assert_dict(json.loads(content), response)
# m.text message type # m.text message type
path = "/rooms/%s/messages/%s/mid2" % ( path = "/rooms/%s/send/m.room.message/mid2" % (
urllib.quote(self.room_id), self.user_id urllib.quote(self.room_id))
)
content = '{"body":"test2","msgtype":"m.text"}' content = '{"body":"test2","msgtype":"m.text"}'
(code, response) = yield self.mock_resource.trigger("PUT", path, content) (code, response) = yield self.mock_resource.trigger("PUT", path, content)
self.assertEquals(200, code, msg=str(response)) self.assertEquals(200, code, msg=str(response))
@ -933,11 +930,3 @@ class RoomMessagesTestCase(RestTestCase):
# (code, response) = yield self.mock_resource.trigger("GET", path, None) # (code, response) = yield self.mock_resource.trigger("GET", path, None)
# self.assertEquals(200, code, msg=str(response)) # self.assertEquals(200, code, msg=str(response))
# self.assert_dict(json.loads(content), response) # self.assert_dict(json.loads(content), response)
# trying to send message in different user path
path = "/rooms/%s/messages/%s/mid2" % (
urllib.quote(self.room_id), "invalid" + self.user_id
)
content = '{"body":"test2","msgtype":"m.text"}'
(code, response) = yield self.mock_resource.trigger("PUT", path, content)
self.assertEquals(403, code, msg=str(response))

View File

@ -99,14 +99,14 @@ class RestTestCase(unittest.TestCase):
defer.returnValue(response) defer.returnValue(response)
@defer.inlineCallbacks @defer.inlineCallbacks
def send(self, room_id, sender_id, body=None, msg_id=None, tok=None, def send(self, room_id, body=None, txn_id=None, tok=None,
expect_code=200): expect_code=200):
if msg_id is None: if txn_id is None:
msg_id = "m%s" % (str(time.time())) txn_id = "m%s" % (str(time.time()))
if body is None: if body is None:
body = "body_text_here" body = "body_text_here"
path = "/rooms/%s/messages/%s/%s" % (room_id, sender_id, msg_id) path = "/rooms/%s/send/m.room.message/%s" % (room_id, txn_id)
content = '{"msgtype":"m.text","body":"%s"}' % body content = '{"msgtype":"m.text","body":"%s"}' % body
if tok: if tok:
path = path + "?access_token=%s" % tok path = path + "?access_token=%s" % tok

View File

@ -162,12 +162,12 @@ angular.module('matrixService', [])
return doRequest("GET", path, undefined, {}); return doRequest("GET", path, undefined, {});
}, },
sendMessage: function(room_id, msg_id, content) { sendMessage: function(room_id, txn_id, content) {
// The REST path spec // The REST path spec
var path = "/rooms/$room_id/messages/$from/$msg_id"; var path = "/rooms/$room_id/send/m.room.message/$txn_id";
if (!msg_id) { if (!txn_id) {
msg_id = "m" + new Date().getTime(); txn_id = "m" + new Date().getTime();
} }
// Like the cmd client, escape room ids // Like the cmd client, escape room ids
@ -175,8 +175,7 @@ angular.module('matrixService', [])
// Customize it // Customize it
path = path.replace("$room_id", room_id); path = path.replace("$room_id", room_id);
path = path.replace("$from", config.user_id); path = path.replace("$txn_id", txn_id);
path = path.replace("$msg_id", msg_id);
return doRequest("PUT", path, undefined, content); return doRequest("PUT", path, undefined, content);
}, },