diff --git a/CHANGES.rst b/CHANGES.rst
index 813ad364e..297ae914f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,11 @@
+Changes in synapse 0.6.1 (2015-01-07)
+=====================================
+
+ * Major optimizations to improve performance of initial sync and event sending
+ in large rooms (by up to 10x)
+ * Media repository now includes a Content-Length header on media downloads.
+ * Improve quality of thumbnails by changing resizing algorithm.
+
Changes in synapse 0.6.0 (2014-12-16)
=====================================
diff --git a/README.rst b/README.rst
index 92b94bcd7..768da3df6 100644
--- a/README.rst
+++ b/README.rst
@@ -108,6 +108,15 @@ To install the synapse homeserver run::
This installs synapse, along with the libraries it uses, into
``$HOME/.local/lib/`` on Linux or ``$HOME/Library/Python/2.7/lib/`` on OSX.
+Your python may not give priority to locally installed libraries over system
+libraries, in which case you must add your local packages to your python path::
+
+ $ # on Linux:
+ $ export PYTHONPATH=$HOME/.local/lib/python2.7/site-packages:$PYTHONPATH
+
+ $ # on OSX:
+ $ export PYTHONPATH=$HOME/Library/Python/2.7/lib/python/site-packages:$PYTHONPATH
+
For reliable VoIP calls to be routed via this homeserver, you MUST configure
a TURN server. See docs/turn-howto.rst for details.
diff --git a/UPGRADE.rst b/UPGRADE.rst
index 9618ad2d5..0f81f3e11 100644
--- a/UPGRADE.rst
+++ b/UPGRADE.rst
@@ -52,7 +52,7 @@ resulting conflicts during the upgrade process.
Before running the command the homeserver should be first completely
shutdown. To run it, simply specify the location of the database, e.g.:
- ./database-prepare-for-0.5.0.sh "homeserver.db"
+ ./scripts/database-prepare-for-0.5.0.sh "homeserver.db"
Once this has successfully completed it will be safe to restart the
homeserver. You may notice that the homeserver takes a few seconds longer to
@@ -147,7 +147,7 @@ rooms the home server was a member of and room alias mappings.
Before running the command the homeserver should be first completely
shutdown. To run it, simply specify the location of the database, e.g.:
- ./database-prepare-for-0.0.1.sh "homeserver.db"
+ ./scripts/database-prepare-for-0.0.1.sh "homeserver.db"
Once this has successfully completed it will be safe to restart the
homeserver. You may notice that the homeserver takes a few seconds longer to
diff --git a/VERSION b/VERSION
index a918a2aa1..3b3e72317 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.0
+0.6.1b
diff --git a/cmdclient/console.py b/contrib/cmdclient/console.py
similarity index 100%
rename from cmdclient/console.py
rename to contrib/cmdclient/console.py
diff --git a/cmdclient/http.py b/contrib/cmdclient/http.py
similarity index 100%
rename from cmdclient/http.py
rename to contrib/cmdclient/http.py
diff --git a/experiments/cursesio.py b/contrib/experiments/cursesio.py
similarity index 100%
rename from experiments/cursesio.py
rename to contrib/experiments/cursesio.py
diff --git a/experiments/test_messaging.py b/contrib/experiments/test_messaging.py
similarity index 100%
rename from experiments/test_messaging.py
rename to contrib/experiments/test_messaging.py
diff --git a/graph/graph.py b/contrib/graph/graph.py
similarity index 100%
rename from graph/graph.py
rename to contrib/graph/graph.py
diff --git a/graph/graph2.py b/contrib/graph/graph2.py
similarity index 84%
rename from graph/graph2.py
rename to contrib/graph/graph2.py
index b9b8a562a..6b551d42e 100644
--- a/graph/graph2.py
+++ b/contrib/graph/graph2.py
@@ -23,14 +23,27 @@ import argparse
from synapse.events import FrozenEvent
-def make_graph(db_name, room_id, file_prefix):
+def make_graph(db_name, room_id, file_prefix, limit):
conn = sqlite3.connect(db_name)
- c = conn.execute(
- "SELECT json FROM event_json where room_id = ?",
- (room_id,)
+ sql = (
+ "SELECT json FROM event_json as j "
+ "INNER JOIN events as e ON e.event_id = j.event_id "
+ "WHERE j.room_id = ?"
)
+ args = [room_id]
+
+ if limit:
+ sql += (
+ " ORDER BY topological_ordering DESC, stream_ordering DESC "
+ "LIMIT ?"
+ )
+
+ args.append(limit)
+
+ c = conn.execute(sql, args)
+
events = [FrozenEvent(json.loads(e[0])) for e in c.fetchall()]
events.sort(key=lambda e: e.depth)
@@ -128,11 +141,16 @@ if __name__ == "__main__":
)
parser.add_argument(
"-p", "--prefix", dest="prefix",
- help="String to prefix output files with"
+ help="String to prefix output files with",
+ default="graph_output"
+ )
+ parser.add_argument(
+ "-l", "--limit",
+ help="Only retrieve the last N events.",
)
parser.add_argument('db')
parser.add_argument('room')
args = parser.parse_args()
- make_graph(args.db, args.room, args.prefix)
+ make_graph(args.db, args.room, args.prefix, args.limit)
diff --git a/jsfiddles/create_room_send_msg/demo.css b/jsfiddles/create_room_send_msg/demo.css
deleted file mode 100644
index 48a55f372..000000000
--- a/jsfiddles/create_room_send_msg/demo.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.loggedin {
- visibility: hidden;
-}
-
-p {
- font-family: monospace;
-}
-
-table
-{
- border-spacing:5px;
-}
-
-th,td
-{
- padding:5px;
-}
diff --git a/jsfiddles/create_room_send_msg/demo.html b/jsfiddles/create_room_send_msg/demo.html
deleted file mode 100644
index 088ff7ac0..000000000
--- a/jsfiddles/create_room_send_msg/demo.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
This room creation / message sending demo requires a home server to be running on http://localhost:8008
-
-
-
-
-
-
-
-
- Room ID |
- My state |
- Room Alias |
- Latest message |
-
-
-
-
-
diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js
deleted file mode 100644
index 9c346e2f6..000000000
--- a/jsfiddles/create_room_send_msg/demo.js
+++ /dev/null
@@ -1,113 +0,0 @@
-var accountInfo = {};
-
-var showLoggedIn = function(data) {
- accountInfo = data;
- getCurrentRoomList();
- $(".loggedin").css({visibility: "visible"});
-};
-
-$('.login').live('click', function() {
- var user = $("#userLogin").val();
- var password = $("#passwordLogin").val();
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/login",
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
- dataType: "json",
- success: function(data) {
- showLoggedIn(data);
- },
- error: function(err) {
- var errMsg = "To try this, you need a home server running!";
- var errJson = $.parseJSON(err.responseText);
- if (errJson) {
- errMsg = JSON.stringify(errJson);
- }
- alert(errMsg);
- }
- });
-});
-
-var getCurrentRoomList = function() {
- var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
- $.getJSON(url, function(data) {
- var rooms = data.rooms;
- for (var i=0; i 0) {
- data.room_alias_name = roomAlias;
- }
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify(data),
- dataType: "json",
- success: function(data) {
- data.membership = "join"; // you are automatically joined into every room you make.
- data.latest_message = "";
- addRoom(data);
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
-});
-
-var addRoom = function(data) {
- row = "" +
- ""+data.room_id+" | " +
- ""+data.membership+" | " +
- ""+data.room_alias+" | " +
- ""+data.latest_message+" | " +
- "
";
- $("#rooms").append(row);
-};
-
-$('.sendMessage').live('click', function() {
- var roomId = $("#roomId").val();
- var body = $("#messageBody").val();
- var msgId = $.now();
-
- if (roomId.length === 0 || body.length === 0) {
- return;
- }
-
- var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$roomid", encodeURIComponent(roomId));
-
- var data = {
- msgtype: "m.text",
- body: body
- };
-
- $.ajax({
- url: url,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify(data),
- dataType: "json",
- success: function(data) {
- $("#messageBody").val("");
- // wipe the table and reload it. Using the event stream would be the best
- // solution but that is out of scope of this fiddle.
- $("#rooms").find("tr:gt(0)").remove();
- getCurrentRoomList();
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
-});
diff --git a/jsfiddles/event_stream/demo.css b/jsfiddles/event_stream/demo.css
deleted file mode 100644
index 48a55f372..000000000
--- a/jsfiddles/event_stream/demo.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.loggedin {
- visibility: hidden;
-}
-
-p {
- font-family: monospace;
-}
-
-table
-{
- border-spacing:5px;
-}
-
-th,td
-{
- padding:5px;
-}
diff --git a/jsfiddles/event_stream/demo.html b/jsfiddles/event_stream/demo.html
deleted file mode 100644
index 7657780d2..000000000
--- a/jsfiddles/event_stream/demo.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
This event stream demo requires a home server to be running on http://localhost:8008
-
-
-
-
-
-
-
-
- Room ID |
- Latest message |
-
-
-
-
-
diff --git a/jsfiddles/event_stream/demo.js b/jsfiddles/event_stream/demo.js
deleted file mode 100644
index acba8391f..000000000
--- a/jsfiddles/event_stream/demo.js
+++ /dev/null
@@ -1,145 +0,0 @@
-var accountInfo = {};
-
-var eventStreamInfo = {
- from: "END"
-};
-
-var roomInfo = [];
-
-var longpollEventStream = function() {
- var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$from", eventStreamInfo.from);
-
- $.getJSON(url, function(data) {
- eventStreamInfo.from = data.end;
-
- var hasNewLatestMessage = false;
- for (var i=0; i"+roomList[i].room_id+"" +
- ""+roomList[i].latest_message+" | " +
- "";
- rows += row;
- }
-
- $("#rooms").append(rows);
-};
-
diff --git a/jsfiddles/example_app/demo.css b/jsfiddles/example_app/demo.css
deleted file mode 100644
index 4c1e157cc..000000000
--- a/jsfiddles/example_app/demo.css
+++ /dev/null
@@ -1,43 +0,0 @@
-.roomListDashboard, .roomContents, .sendMessageForm {
- visibility: hidden;
-}
-
-.roomList {
- background-color: #909090;
-}
-
-.messageWrapper {
- background-color: #EEEEEE;
- height: 400px;
- overflow: scroll;
-}
-
-.membersWrapper {
- background-color: #EEEEEE;
- height: 200px;
- width: 50%;
- overflow: scroll;
-}
-
-.textEntry {
- width: 100%
-}
-
-p {
- font-family: monospace;
-}
-
-table
-{
- border-spacing:5px;
-}
-
-th,td
-{
- padding:5px;
-}
-
-.roomList tr:not(:first-child):hover {
- background-color: orange;
- cursor: pointer;
-}
diff --git a/jsfiddles/example_app/demo.details b/jsfiddles/example_app/demo.details
deleted file mode 100644
index 3f96d3e74..000000000
--- a/jsfiddles/example_app/demo.details
+++ /dev/null
@@ -1,7 +0,0 @@
- name: Example Matrix Client
- description: Includes login, live event streaming, creating rooms, sending messages and viewing member lists.
- authors:
- - matrix.org
- resources:
- - http://matrix.org
- normalize_css: no
\ No newline at end of file
diff --git a/jsfiddles/example_app/demo.html b/jsfiddles/example_app/demo.html
deleted file mode 100644
index 7a9dffddd..000000000
--- a/jsfiddles/example_app/demo.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
Matrix example application: Requires a local home server running at http://localhost:8008
-
-
-
-
-
-
-
-
-
- Room |
- My state |
- Latest message |
-
-
-
-
-
-
-
-
-
diff --git a/jsfiddles/example_app/demo.js b/jsfiddles/example_app/demo.js
deleted file mode 100644
index 13c9c2b33..000000000
--- a/jsfiddles/example_app/demo.js
+++ /dev/null
@@ -1,327 +0,0 @@
-var accountInfo = {};
-
-var eventStreamInfo = {
- from: "END"
-};
-
-var roomInfo = [];
-var memberInfo = [];
-var viewingRoomId;
-
-// ************** Event Streaming **************
-var longpollEventStream = function() {
- var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$from", eventStreamInfo.from);
-
- $.getJSON(url, function(data) {
- eventStreamInfo.from = data.end;
-
- var hasNewLatestMessage = false;
- var updatedMemberList = false;
- var i=0;
- var j=0;
- for (i=0; i 0) {
- data.room_alias_name = roomAlias;
- }
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify(data),
- dataType: "json",
- success: function(response) {
- $("#roomAlias").val("");
- response.membership = "join"; // you are automatically joined into every room you make.
- response.latest_message = "";
-
- roomInfo.push(response);
- setRooms(roomInfo);
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
-});
-
-// ************** Getting current state **************
-var getCurrentRoomList = function() {
- var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
- $.getJSON(url, function(data) {
- var rooms = data.rooms;
- for (var i=0; i=0; --i) {
- addMessage(data.chunk[i]);
- }
- });
-};
-
-var getMemberList = function(roomId) {
- $("#members").empty();
- memberInfo = [];
- var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
- encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token;
- $.getJSON(url, function(data) {
- for (var i=0; i"+roomList[i].room_id+"" +
- ""+roomList[i].membership+" | " +
- ""+roomList[i].latest_message+" | " +
- "";
- rows += row;
- }
-
- $("#rooms").append(rows);
-
- $('#rooms').find("tr").click(function(){
- var roomId = $(this).find('td:eq(0)').text();
- var membership = $(this).find('td:eq(1)').text();
- if (membership !== "join") {
- console.log("Joining room " + roomId);
- var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/join?access_token=$token";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$roomid", encodeURIComponent(roomId));
- $.ajax({
- url: url,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({membership: "join"}),
- dataType: "json",
- success: function(data) {
- loadRoomContent(roomId);
- getCurrentRoomList();
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
- }
- else {
- loadRoomContent(roomId);
- }
- });
-};
-
-var addMessage = function(data) {
-
- var msg = data.content.body;
- if (data.type === "m.room.member") {
- if (data.content.membership === undefined) {
- return;
- }
- if (data.content.membership === "invite") {
- msg = "invited " + data.state_key + " to the room";
- }
- else if (data.content.membership === "join") {
- msg = "joined the room";
- }
- else if (data.content.membership === "leave") {
- msg = "left the room";
- }
- else if (data.content.membership === "ban") {
- msg = "was banned from the room";
- }
- }
- if (msg === undefined) {
- return;
- }
-
- var row = "" +
- ""+data.user_id+" | " +
- ""+msg+" | " +
- "
";
- $("#messages").append(row);
-};
-
-var addMember = function(data) {
- var row = "" +
- ""+data.state_key+" | " +
- ""+data.content.membership+" | " +
- "
";
- $("#members").append(row);
-};
-
diff --git a/jsfiddles/register_login/demo.css b/jsfiddles/register_login/demo.css
deleted file mode 100644
index 11781c250..000000000
--- a/jsfiddles/register_login/demo.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.loggedin {
- visibility: hidden;
-}
-
-p {
- font-family: monospace;
-}
diff --git a/jsfiddles/register_login/demo.html b/jsfiddles/register_login/demo.html
deleted file mode 100644
index fcac453ac..000000000
--- a/jsfiddles/register_login/demo.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
This registration/login demo requires a home server to be running on http://localhost:8008
-
-
-
-
-
diff --git a/jsfiddles/register_login/demo.js b/jsfiddles/register_login/demo.js
deleted file mode 100644
index 2e6957b63..000000000
--- a/jsfiddles/register_login/demo.js
+++ /dev/null
@@ -1,79 +0,0 @@
-var accountInfo = {};
-
-var showLoggedIn = function(data) {
- accountInfo = data;
- $(".loggedin").css({visibility: "visible"});
- $("#welcomeText").text("Welcome " + accountInfo.user_id+". Your access token is: " +
- accountInfo.access_token);
-};
-
-$('.register').live('click', function() {
- var user = $("#user").val();
- var password = $("#password").val();
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/register",
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
- dataType: "json",
- success: function(data) {
- showLoggedIn(data);
- },
- error: function(err) {
- var errMsg = "To try this, you need a home server running!";
- var errJson = $.parseJSON(err.responseText);
- if (errJson) {
- errMsg = JSON.stringify(errJson);
- }
- alert(errMsg);
- }
- });
-});
-
-var login = function(user, password) {
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/login",
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
- dataType: "json",
- success: function(data) {
- showLoggedIn(data);
- },
- error: function(err) {
- var errMsg = "To try this, you need a home server running!";
- var errJson = $.parseJSON(err.responseText);
- if (errJson) {
- errMsg = JSON.stringify(errJson);
- }
- alert(errMsg);
- }
- });
-};
-
-$('.login').live('click', function() {
- var user = $("#userLogin").val();
- var password = $("#passwordLogin").val();
- $.getJSON("http://localhost:8008/_matrix/client/api/v1/login", function(data) {
- if (data.flows[0].type !== "m.login.password") {
- alert("I don't know how to login with this type: " + data.type);
- return;
- }
- login(user, password);
- });
-});
-
-$('.logout').live('click', function() {
- accountInfo = {};
- $("#imSyncText").text("");
- $(".loggedin").css({visibility: "hidden"});
-});
-
-$('.testToken').live('click', function() {
- var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
- $.getJSON(url, function(data) {
- $("#imSyncText").text(JSON.stringify(data, undefined, 2));
- }).fail(function(err) {
- $("#imSyncText").text(JSON.stringify($.parseJSON(err.responseText)));
- });
-});
diff --git a/jsfiddles/room_memberships/demo.css b/jsfiddles/room_memberships/demo.css
deleted file mode 100644
index 48a55f372..000000000
--- a/jsfiddles/room_memberships/demo.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.loggedin {
- visibility: hidden;
-}
-
-p {
- font-family: monospace;
-}
-
-table
-{
- border-spacing:5px;
-}
-
-th,td
-{
- padding:5px;
-}
diff --git a/jsfiddles/room_memberships/demo.html b/jsfiddles/room_memberships/demo.html
deleted file mode 100644
index e6f39df5a..000000000
--- a/jsfiddles/room_memberships/demo.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
This room membership demo requires a home server to be running on http://localhost:8008
-
-
-
-
-
-
-
-
-
- Room ID |
- My state |
- Room Alias |
-
-
-
-
-
diff --git a/jsfiddles/room_memberships/demo.js b/jsfiddles/room_memberships/demo.js
deleted file mode 100644
index 8a7b1aa88..000000000
--- a/jsfiddles/room_memberships/demo.js
+++ /dev/null
@@ -1,141 +0,0 @@
-var accountInfo = {};
-
-var showLoggedIn = function(data) {
- accountInfo = data;
- getCurrentRoomList();
- $(".loggedin").css({visibility: "visible"});
- $("#membership").change(function() {
- if ($("#membership").val() === "invite") {
- $("#targetUser").css({visibility: "visible"});
- }
- else {
- $("#targetUser").css({visibility: "hidden"});
- }
-});
-};
-
-$('.login').live('click', function() {
- var user = $("#userLogin").val();
- var password = $("#passwordLogin").val();
- $.ajax({
- url: "http://localhost:8008/_matrix/client/api/v1/login",
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
- dataType: "json",
- success: function(data) {
- $("#rooms").find("tr:gt(0)").remove();
- showLoggedIn(data);
- },
- error: function(err) {
- var errMsg = "To try this, you need a home server running!";
- var errJson = $.parseJSON(err.responseText);
- if (errJson) {
- errMsg = JSON.stringify(errJson);
- }
- alert(errMsg);
- }
- });
-});
-
-var getCurrentRoomList = function() {
- $("#roomId").val("");
- // wipe the table and reload it. Using the event stream would be the best
- // solution but that is out of scope of this fiddle.
- $("#rooms").find("tr:gt(0)").remove();
-
- var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
- $.getJSON(url, function(data) {
- var rooms = data.rooms;
- for (var i=0; i"+data.room_id+"" +
- ""+data.membership+" | " +
- ""+data.room_alias+" | " +
- "";
- $("#rooms").append(row);
-};
-
-$('.changeMembership').live('click', function() {
- var roomId = $("#roomId").val();
- var member = $("#targetUser").val();
- var membership = $("#membership").val();
-
- if (roomId.length === 0) {
- return;
- }
-
- var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/$membership?access_token=$token";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$roomid", encodeURIComponent(roomId));
- url = url.replace("$membership", membership);
-
- var data = {};
-
- if (membership === "invite") {
- data = {
- user_id: member
- };
- }
-
- $.ajax({
- url: url,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify(data),
- dataType: "json",
- success: function(data) {
- getCurrentRoomList();
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
-});
-
-$('.joinAlias').live('click', function() {
- var roomAlias = $("#roomAlias").val();
- var url = "http://localhost:8008/_matrix/client/api/v1/join/$roomalias?access_token=$token";
- url = url.replace("$token", accountInfo.access_token);
- url = url.replace("$roomalias", encodeURIComponent(roomAlias));
- $.ajax({
- url: url,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify({}),
- dataType: "json",
- success: function(data) {
- getCurrentRoomList();
- },
- error: function(err) {
- alert(JSON.stringify($.parseJSON(err.responseText)));
- }
- });
-});
diff --git a/scripts/copyrighter-sql.pl b/scripts/copyrighter-sql.pl
new file mode 100755
index 000000000..890e51e58
--- /dev/null
+++ b/scripts/copyrighter-sql.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl -pi
+# Copyright 2015 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$copyright = <=14.0.0": ["twisted>=14.0.0"],
+ "service_identity>=1.0.0": ["service_identity>=1.0.0"],
+ "pyopenssl>=0.14": ["OpenSSL>=0.14"],
+ "pyyaml": ["yaml"],
+ "pyasn1": ["pyasn1"],
+ "pynacl": ["nacl"],
+ "daemonize": ["daemonize"],
+ "py-bcrypt": ["bcrypt"],
+ "frozendict>=0.4": ["frozendict"],
+ "pillow": ["PIL"],
+}
+
+
+class MissingRequirementError(Exception):
+ pass
+
+
+def check_requirements():
+ """Checks that all the modules needed by synapse have been correctly
+ installed and are at the correct version"""
+ for dependency, module_requirements in REQUIREMENTS.items():
+ for module_requirement in module_requirements:
+ if ">=" in module_requirement:
+ module_name, required_version = module_requirement.split(">=")
+ version_test = ">="
+ elif "==" in module_requirement:
+ module_name, required_version = module_requirement.split("==")
+ version_test = "=="
+ else:
+ module_name = module_requirement
+ version_test = None
+
+ try:
+ module = __import__(module_name)
+ except ImportError:
+ logging.exception(
+ "Can't import %r which is part of %r",
+ module_name, dependency
+ )
+ raise MissingRequirementError(
+ "Can't import %r which is part of %r"
+ % (module_name, dependency)
+ )
+ version = getattr(module, "__version__", None)
+ file_path = getattr(module, "__file__", None)
+ logger.info(
+ "Using %r version %r from %r to satisfy %r",
+ module_name, version, file_path, dependency
+ )
+
+ if version_test == ">=":
+ if version is None:
+ raise MissingRequirementError(
+ "Version of %r isn't set as __version__ of module %r"
+ % (dependency, module_name)
+ )
+ if LooseVersion(version) < LooseVersion(required_version):
+ raise MissingRequirementError(
+ "Version of %r in %r is too old. %r < %r"
+ % (dependency, file_path, version, required_version)
+ )
+ elif version_test == "==":
+ if version is None:
+ raise MissingRequirementError(
+ "Version of %r isn't set as __version__ of module %r"
+ % (dependency, module_name)
+ )
+ if LooseVersion(version) != LooseVersion(required_version):
+ raise MissingRequirementError(
+ "Unexpected version of %r in %r. %r != %r"
+ % (dependency, file_path, version, required_version)
+ )
diff --git a/synapse/rest/events.py b/synapse/rest/events.py
index cf6d13f81..bedcb2bcc 100644
--- a/synapse/rest/events.py
+++ b/synapse/rest/events.py
@@ -44,8 +44,11 @@ class EventStreamRestServlet(RestServlet):
except ValueError:
raise SynapseError(400, "timeout must be in milliseconds.")
+ as_client_event = "raw" not in request.args
+
chunk = yield handler.get_stream(
- auth_user.to_string(), pagin_config, timeout=timeout
+ auth_user.to_string(), pagin_config, timeout=timeout,
+ as_client_event=as_client_event
)
except:
logger.exception("Event stream failed")
diff --git a/synapse/rest/initial_sync.py b/synapse/rest/initial_sync.py
index a57158958..b13d56b28 100644
--- a/synapse/rest/initial_sync.py
+++ b/synapse/rest/initial_sync.py
@@ -27,12 +27,15 @@ class InitialSyncRestServlet(RestServlet):
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)
+ feedback=with_feedback,
+ as_client_event=as_client_event
+ )
defer.returnValue((200, content))
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index e40773758..48bba2a5f 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -246,7 +246,7 @@ class JoinRoomAliasServlet(RestServlet):
}
)
- defer.returnValue((200, {}))
+ defer.returnValue((200, {"room_id": identifier.to_string()}))
@defer.inlineCallbacks
def on_PUT(self, request, room_identifier, txn_id):
@@ -314,12 +314,15 @@ class RoomMessageListRestServlet(RestServlet):
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)
+ feedback=with_feedback,
+ as_client_event=as_client_event
+ )
defer.returnValue((200, msgs))
diff --git a/synapse/server.py b/synapse/server.py
index c3bf46abb..d861efd2f 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -149,8 +149,8 @@ class BaseHomeServer(object):
object."""
return EventID.from_string(s)
- def serialize_event(self, e):
- return serialize_event(self, e)
+ def serialize_event(self, e, as_client_event=True):
+ return serialize_event(self, e, as_client_event)
def get_ip_from_request(self, request):
# May be an X-Forwarding-For header depending on config
diff --git a/synapse/storage/room.py b/synapse/storage/room.py
index 978b2c4a4..6542f8e4f 100644
--- a/synapse/storage/room.py
+++ b/synapse/storage/room.py
@@ -58,13 +58,6 @@ class RoomStore(SQLBaseStore):
logger.error("store_room with room_id=%s failed: %s", room_id, e)
raise StoreError(500, "Problem creating room.")
- def store_room_config(self, room_id, visibility):
- return self._simple_update_one(
- table=RoomsTable.table_name,
- keyvalues={"room_id": room_id},
- updatevalues={"is_public": visibility}
- )
-
def get_room(self, room_id):
"""Retrieve a room.
diff --git a/synapse/storage/state.py b/synapse/storage/state.py
index 532751770..71db16d0e 100644
--- a/synapse/storage/state.py
+++ b/synapse/storage/state.py
@@ -78,12 +78,6 @@ class StateStore(SQLBaseStore):
f,
)
- def store_state_groups(self, event):
- return self.runInteraction(
- "store_state_groups",
- self._store_state_groups_txn, event
- )
-
def _store_state_groups_txn(self, txn, event, context):
if context.current_state is None:
return
diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py
index bedc3c6c5..8ac2adab0 100644
--- a/synapse/storage/stream.py
+++ b/synapse/storage/stream.py
@@ -39,6 +39,8 @@ from ._base import SQLBaseStore
from synapse.api.errors import SynapseError
from synapse.util.logutils import log_function
+from collections import namedtuple
+
import logging
@@ -52,91 +54,79 @@ _STREAM_TOKEN = "stream"
_TOPOLOGICAL_TOKEN = "topological"
-def _parse_stream_token(string):
- try:
- if string[0] != 's':
- raise
- return int(string[1:])
- except:
- raise SynapseError(400, "Invalid token")
+class _StreamToken(namedtuple("_StreamToken", "topological stream")):
+ """Tokens are positions between events. The token "s1" comes after event 1.
+ s0 s1
+ | |
+ [0] V [1] V [2]
-def _parse_topological_token(string):
- try:
- if string[0] != 't':
- raise
- parts = string[1:].split('-', 1)
- return (int(parts[0]), int(parts[1]))
- except:
- raise SynapseError(400, "Invalid token")
+ Tokens can either be a point in the live event stream or a cursor going
+ through historic events.
+ When traversing the live event stream events are ordered by when they
+ arrived at the homeserver.
-def is_stream_token(string):
- try:
- _parse_stream_token(string)
- return True
- except:
- return False
+ When traversing historic events the events are ordered by their depth in
+ the event graph "topological_ordering" and then by when they arrived at the
+ homeserver "stream_ordering".
+ Live tokens start with an "s" followed by the "stream_ordering" id of the
+ event it comes after. Historic tokens start with a "t" followed by the
+ "topological_ordering" id of the event it comes after, follewed by "-",
+ followed by the "stream_ordering" id of the event it comes after.
+ """
+ __slots__ = []
-def is_topological_token(string):
- try:
- _parse_topological_token(string)
- return True
- except:
- return False
+ @classmethod
+ def parse(cls, string):
+ try:
+ if string[0] == 's':
+ return cls(None, int(string[1:]))
+ if string[0] == 't':
+ parts = string[1:].split('-', 1)
+ return cls(int(parts[1]), int(parts[0]))
+ except:
+ pass
+ raise SynapseError(400, "Invalid token %r" % (string,))
+ @classmethod
+ def parse_stream_token(cls, string):
+ try:
+ if string[0] == 's':
+ return cls(None, int(string[1:]))
+ except:
+ pass
+ raise SynapseError(400, "Invalid token %r" % (string,))
-def _get_token_bound(token, comparison):
- try:
- s = _parse_stream_token(token)
- return "%s %s %d" % ("stream_ordering", comparison, s)
- except:
- pass
+ def __str__(self):
+ if self.topological is not None:
+ return "t%d-%d" % (self.topological, self.stream)
+ else:
+ return "s%d" % (self.stream,)
- try:
- top, stream = _parse_topological_token(token)
- return "%s %s %d AND %s %s %d" % (
- "topological_ordering", comparison, top,
- "stream_ordering", comparison, stream,
- )
- except:
- pass
+ def lower_bound(self):
+ if self.topological is None:
+ return "(%d < %s)" % (self.stream, "stream_ordering")
+ else:
+ return "(%d < %s OR (%d == %s AND %d < %s))" % (
+ self.topological, "topological_ordering",
+ self.topological, "topological_ordering",
+ self.stream, "stream_ordering",
+ )
- raise SynapseError(400, "Invalid token")
+ def upper_bound(self):
+ if self.topological is None:
+ return "(%d >= %s)" % (self.stream, "stream_ordering")
+ else:
+ return "(%d > %s OR (%d == %s AND %d >= %s))" % (
+ self.topological, "topological_ordering",
+ self.topological, "topological_ordering",
+ self.stream, "stream_ordering",
+ )
class StreamStore(SQLBaseStore):
- @log_function
- def get_room_events(self, user_id, from_key, to_key, room_id, limit=0,
- direction='f', with_feedback=False):
- # We deal with events request in two different ways depending on if
- # this looks like an /events request or a pagination request.
- is_events = (
- direction == 'f'
- and user_id
- and is_stream_token(from_key)
- and to_key and is_stream_token(to_key)
- )
-
- if is_events:
- return self.get_room_events_stream(
- user_id=user_id,
- from_key=from_key,
- to_key=to_key,
- room_id=room_id,
- limit=limit,
- with_feedback=with_feedback,
- )
- else:
- return self.paginate_room_events(
- from_key=from_key,
- to_key=to_key,
- room_id=room_id,
- limit=limit,
- with_feedback=with_feedback,
- )
-
@log_function
def get_room_events_stream(self, user_id, from_key, to_key, room_id,
limit=0, with_feedback=False):
@@ -162,8 +152,8 @@ class StreamStore(SQLBaseStore):
limit = MAX_STREAM_SIZE
# From and to keys should be integers from ordering.
- from_id = _parse_stream_token(from_key)
- to_id = _parse_stream_token(to_key)
+ from_id = _StreamToken.parse_stream_token(from_key)
+ to_id = _StreamToken.parse_stream_token(to_key)
if from_key == to_key:
return defer.succeed(([], to_key))
@@ -181,7 +171,7 @@ class StreamStore(SQLBaseStore):
}
def f(txn):
- txn.execute(sql, (user_id, user_id, from_id, to_id,))
+ txn.execute(sql, (user_id, user_id, from_id.stream, to_id.stream,))
rows = self.cursor_to_dict(txn)
@@ -211,17 +201,21 @@ class StreamStore(SQLBaseStore):
# 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.
- from_comp = '<=' if direction == 'b' else '>'
- to_comp = '>' if direction == 'b' else '<='
- order = "DESC" if direction == 'b' else "ASC"
-
args = [room_id]
-
- bounds = _get_token_bound(from_key, from_comp)
- if to_key:
- bounds = "%s AND %s" % (
- bounds, _get_token_bound(to_key, to_comp)
- )
+ if direction == 'b':
+ order = "DESC"
+ bounds = _StreamToken.parse(from_key).upper_bound()
+ if to_key:
+ bounds = "%s AND %s" % (
+ bounds, _StreamToken.parse(to_key).lower_bound()
+ )
+ else:
+ order = "ASC"
+ bounds = _StreamToken.parse(from_key).lower_bound()
+ if to_key:
+ bounds = "%s AND %s" % (
+ bounds, _StreamToken.parse(to_key).upper_bound()
+ )
if int(limit) > 0:
args.append(int(limit))
@@ -249,9 +243,13 @@ class StreamStore(SQLBaseStore):
topo = rows[-1]["topological_ordering"]
toke = rows[-1]["stream_ordering"]
if direction == 'b':
- topo -= 1
+ # Tokens are positions between events.
+ # This token points *after* the last event in the chunk.
+ # We need it to point to the event before it in the chunk
+ # when we are going backwards so we subtract one from the
+ # stream part.
toke -= 1
- next_token = "t%s-%s" % (topo, toke)
+ next_token = str(_StreamToken(topo, toke))
else:
# TODO (erikj): We should work out what to do here instead.
next_token = to_key if to_key else from_key
@@ -284,9 +282,14 @@ class StreamStore(SQLBaseStore):
rows.reverse() # As we selected with reverse ordering
if rows:
+ # Tokens are positions between events.
+ # This token points *after* the last event in the chunk.
+ # We need it to point to the event before it in the chunk
+ # since we are going backwards so we subtract one from the
+ # stream part.
topo = rows[0]["topological_ordering"]
- toke = rows[0]["stream_ordering"]
- start_token = "t%s-%s" % (topo, toke)
+ toke = rows[0]["stream_ordering"] - 1
+ start_token = str(_StreamToken(topo, toke))
token = (start_token, end_token)
else:
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index b85a89052..c309fbb05 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -59,23 +59,29 @@ class JustPresenceHandlers(object):
def __init__(self, hs):
self.presence_handler = PresenceHandler(hs)
-class PresenceStateTestCase(unittest.TestCase):
- """ Tests presence management. """
+class PresenceTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
db_pool = SQLiteMemoryDbPool()
yield db_pool.prepare()
+ self.clock = MockClock()
+
self.mock_config = NonCallableMock()
self.mock_config.signing_key = [MockKey()]
+ self.mock_federation_resource = MockHttpResource()
+
+ self.mock_http_client = Mock(spec=[])
+ self.mock_http_client.put_json = DeferredMockCallable()
+
hs = HomeServer("test",
- clock=MockClock(),
+ clock=self.clock,
db_pool=db_pool,
handlers=None,
- resource_for_federation=Mock(),
- http_client=None,
+ resource_for_federation=self.mock_federation_resource,
+ http_client=self.mock_http_client,
config=self.mock_config,
keyring=Mock(),
)
@@ -92,24 +98,33 @@ class PresenceStateTestCase(unittest.TestCase):
self.u_banana = hs.parse_userid("@banana:test")
self.u_clementine = hs.parse_userid("@clementine:test")
- yield self.store.create_presence(self.u_apple.localpart)
+ for u in self.u_apple, self.u_banana, self.u_clementine:
+ yield self.store.create_presence(u.localpart)
+
yield self.store.set_presence_state(
self.u_apple.localpart, {"state": ONLINE, "status_msg": "Online"}
)
+ # ID of a local user that does not exist
+ self.u_durian = hs.parse_userid("@durian:test")
+
+ # A remote user
+ self.u_cabbage = hs.parse_userid("@cabbage:elsewhere")
+
self.handler = hs.get_handlers().presence_handler
+ self.room_id = "a-room"
self.room_members = []
def get_rooms_for_user(user):
if user in self.room_members:
- return defer.succeed(["a-room"])
+ return defer.succeed([self.room_id])
else:
return defer.succeed([])
room_member_handler.get_rooms_for_user = get_rooms_for_user
def get_room_members(room_id):
- if room_id == "a-room":
+ if room_id == self.room_id:
return defer.succeed(self.room_members)
else:
return defer.succeed([])
@@ -128,6 +143,10 @@ class PresenceStateTestCase(unittest.TestCase):
self.handler.start_polling_presence = self.mock_start
self.handler.stop_polling_presence = self.mock_stop
+
+class PresenceStateTestCase(PresenceTestCase):
+ """ Tests presence management. """
+
@defer.inlineCallbacks
def test_get_my_state(self):
state = yield self.handler.get_state(
@@ -206,56 +225,9 @@ class PresenceStateTestCase(unittest.TestCase):
self.mock_stop.assert_called_with(self.u_apple)
-class PresenceInvitesTestCase(unittest.TestCase):
+class PresenceInvitesTestCase(PresenceTestCase):
""" Tests presence management. """
- @defer.inlineCallbacks
- def setUp(self):
- self.mock_http_client = Mock(spec=[])
- self.mock_http_client.put_json = DeferredMockCallable()
-
- self.mock_federation_resource = MockHttpResource()
-
- db_pool = SQLiteMemoryDbPool()
- yield db_pool.prepare()
-
- self.mock_config = NonCallableMock()
- self.mock_config.signing_key = [MockKey()]
-
- hs = HomeServer("test",
- clock=MockClock(),
- db_pool=db_pool,
- handlers=None,
- resource_for_client=Mock(),
- resource_for_federation=self.mock_federation_resource,
- http_client=self.mock_http_client,
- config=self.mock_config,
- keyring=Mock(),
- )
- hs.handlers = JustPresenceHandlers(hs)
-
- self.store = hs.get_datastore()
-
- # Some local users to test with
- self.u_apple = hs.parse_userid("@apple:test")
- self.u_banana = hs.parse_userid("@banana:test")
- yield self.store.create_presence(self.u_apple.localpart)
- yield self.store.create_presence(self.u_banana.localpart)
-
- # ID of a local user that does not exist
- self.u_durian = hs.parse_userid("@durian:test")
-
- # A remote user
- self.u_cabbage = hs.parse_userid("@cabbage:elsewhere")
-
- self.handler = hs.get_handlers().presence_handler
-
- self.mock_start = Mock()
- self.mock_stop = Mock()
-
- self.handler.start_polling_presence = self.mock_start
- self.handler.stop_polling_presence = self.mock_stop
-
@defer.inlineCallbacks
def test_invite_local(self):
# TODO(paul): This test will likely break if/when real auth permissions
@@ -558,24 +530,25 @@ class PresencePushTestCase(unittest.TestCase):
])
self.room_member_handler = hs.handlers.room_member_handler
+ self.room_id = "a-room"
self.room_members = []
def get_rooms_for_user(user):
if user in self.room_members:
- return defer.succeed(["a-room"])
+ return defer.succeed([self.room_id])
else:
return defer.succeed([])
self.room_member_handler.get_rooms_for_user = get_rooms_for_user
def get_room_members(room_id):
- if room_id == "a-room":
+ if room_id == self.room_id:
return defer.succeed(self.room_members)
else:
return defer.succeed([])
self.room_member_handler.get_room_members = get_room_members
def get_room_hosts(room_id):
- if room_id == "a-room":
+ if room_id == self.room_id:
hosts = set([u.domain for u in self.room_members])
return defer.succeed(hosts)
else:
@@ -911,7 +884,7 @@ class PresencePushTestCase(unittest.TestCase):
)
yield self.distributor.fire("user_joined_room", self.u_clementine,
- "a-room"
+ self.room_id
)
self.room_members.append(self.u_clementine)
@@ -974,7 +947,7 @@ class PresencePushTestCase(unittest.TestCase):
self.room_members = [self.u_apple, self.u_banana]
yield self.distributor.fire("user_joined_room", self.u_potato,
- "a-room"
+ self.room_id
)
yield put_json.await_calls()
@@ -1003,7 +976,7 @@ class PresencePushTestCase(unittest.TestCase):
self.room_members.append(self.u_potato)
yield self.distributor.fire("user_joined_room", self.u_clementine,
- "a-room"
+ self.room_id
)
put_json.await_calls()
diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py
index 0cb8aa4fb..d3253b48b 100644
--- a/tests/handlers/test_room.py
+++ b/tests/handlers/test_room.py
@@ -223,7 +223,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
yield room_handler.change_membership(event, context)
self.federation.handle_new_event.assert_called_once_with(
- event, None, destinations=set()
+ event, destinations=set()
)
self.datastore.persist_event.assert_called_once_with(
@@ -301,7 +301,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
yield room_handler.change_membership(event, context)
self.federation.handle_new_event.assert_called_once_with(
- event, None, destinations=set(['red'])
+ event, destinations=set(['red'])
)
self.datastore.persist_event.assert_called_once_with(
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index 0d4b368a4..6a498b23a 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -352,3 +352,29 @@ class TypingNotificationsTestCase(unittest.TestCase):
}},
]
)
+
+ # SYN-230 - see if we can still set after timeout
+
+ yield self.handler.started_typing(
+ target_user=self.u_apple,
+ auth_user=self.u_apple,
+ room_id=self.room_id,
+ timeout=10000,
+ )
+
+ self.on_new_user_event.assert_has_calls([
+ call(rooms=[self.room_id]),
+ ])
+ self.on_new_user_event.reset_mock()
+
+ self.assertEquals(self.event_source.get_current_key(), 3)
+ self.assertEquals(
+ self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+ [
+ {"type": "m.typing",
+ "room_id": self.room_id,
+ "content": {
+ "user_ids": [self.u_apple.to_string()],
+ }},
+ ]
+ )
diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py
index 84fd730af..8e65ff9a1 100644
--- a/tests/rest/test_rooms.py
+++ b/tests/rest/test_rooms.py
@@ -294,7 +294,7 @@ class RoomPermissionsTestCase(RestTestCase):
# set [invite/join/left] of self, set [invite/join/left] of other,
# expect all 403s
for usr in [self.user_id, self.rmcreator_id]:
- yield self.join(room=room, user=usr, expect_code=403)
+ yield self.join(room=room, user=usr, expect_code=404)
yield self.leave(room=room, user=usr, expect_code=403)
@defer.inlineCallbacks
diff --git a/tests/rest/test_typing.py b/tests/rest/test_typing.py
index c550294d5..18138af1b 100644
--- a/tests/rest/test_typing.py
+++ b/tests/rest/test_typing.py
@@ -21,7 +21,7 @@ from twisted.internet import defer
import synapse.rest.room
from synapse.server import HomeServer
-from ..utils import MockHttpResource, SQLiteMemoryDbPool, MockKey
+from ..utils import MockHttpResource, MockClock, SQLiteMemoryDbPool, MockKey
from .utils import RestTestCase
from mock import Mock, NonCallableMock
@@ -36,6 +36,8 @@ class RoomTypingTestCase(RestTestCase):
@defer.inlineCallbacks
def setUp(self):
+ self.clock = MockClock()
+
self.mock_resource = MockHttpResource(prefix=PATH_PREFIX)
self.auth_user_id = self.user_id
@@ -47,6 +49,7 @@ class RoomTypingTestCase(RestTestCase):
hs = HomeServer(
"red",
+ clock=self.clock,
db_pool=db_pool,
http_client=None,
replication_layer=Mock(),
@@ -77,6 +80,30 @@ class RoomTypingTestCase(RestTestCase):
return defer.succeed(None)
hs.get_datastore().insert_client_ip = _insert_client_ip
+ def get_room_members(room_id):
+ if room_id == self.room_id:
+ return defer.succeed([hs.parse_userid(self.user_id)])
+ else:
+ return defer.succeed([])
+
+ @defer.inlineCallbacks
+ def fetch_room_distributions_into(room_id, localusers=None,
+ remotedomains=None, ignore_user=None):
+
+ members = yield get_room_members(room_id)
+ for member in members:
+ if ignore_user is not None and member == ignore_user:
+ continue
+
+ if hs.is_mine(member):
+ if localusers is not None:
+ localusers.add(member)
+ else:
+ if remotedomains is not None:
+ remotedomains.add(member.domain)
+ hs.get_handlers().room_member_handler.fetch_room_distributions_into = (
+ fetch_room_distributions_into)
+
synapse.rest.room.register_servlets(hs, self.mock_resource)
self.room_id = yield self.create_room_as(self.user_id)
@@ -113,3 +140,25 @@ class RoomTypingTestCase(RestTestCase):
'{"typing": false}'
)
self.assertEquals(200, code)
+
+ @defer.inlineCallbacks
+ def test_typing_timeout(self):
+ (code, _) = yield self.mock_resource.trigger("PUT",
+ "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+ '{"typing": true, "timeout": 30000}'
+ )
+ self.assertEquals(200, code)
+
+ self.assertEquals(self.event_source.get_current_key(), 1)
+
+ self.clock.advance_time(31);
+
+ self.assertEquals(self.event_source.get_current_key(), 2)
+
+ (code, _) = yield self.mock_resource.trigger("PUT",
+ "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+ '{"typing": true, "timeout": 30000}'
+ )
+ self.assertEquals(200, code)
+
+ self.assertEquals(self.event_source.get_current_key(), 3)
diff --git a/tests/storage/test_room.py b/tests/storage/test_room.py
index 11761fe29..e7739776e 100644
--- a/tests/storage/test_room.py
+++ b/tests/storage/test_room.py
@@ -56,17 +56,6 @@ class RoomStoreTestCase(unittest.TestCase):
(yield self.store.get_room(self.room.to_string()))
)
- @defer.inlineCallbacks
- def test_store_room_config(self):
- yield self.store.store_room_config(self.room.to_string(),
- visibility=False
- )
-
- self.assertObjectHasAttributes(
- {"is_public": False},
- (yield self.store.get_room(self.room.to_string()))
- )
-
@defer.inlineCallbacks
def test_get_rooms(self):
# get_rooms does an INNER JOIN on the room_aliases table :(
diff --git a/tests/unittest.py b/tests/unittest.py
index a9c0e0554..fe26b7574 100644
--- a/tests/unittest.py
+++ b/tests/unittest.py
@@ -69,6 +69,8 @@ class TestCase(unittest.TestCase):
return ret
logging.getLogger().setLevel(level)
+ # Don't set SQL logging
+ logging.getLogger("synapse.storage").setLevel(old_level)
return orig()
def assertObjectHasAttributes(self, attrs, obj):
diff --git a/tests/utils.py b/tests/utils.py
index 731e03f51..97fa8d818 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -138,7 +138,8 @@ class MockClock(object):
now = 1000
def __init__(self):
- # list of tuples of (absolute_time, callback) in no particular order
+ # list of lists of [absolute_time, callback, expired] in no particular
+ # order
self.timers = []
def time(self):
@@ -154,11 +155,16 @@ class MockClock(object):
LoggingContext.thread_local.current_context = current_context
callback()
- t = (self.now + delay, wrapped_callback)
+ t = [self.now + delay, wrapped_callback, False]
self.timers.append(t)
+
return t
def cancel_call_later(self, timer):
+ if timer[2]:
+ raise Exception("Cannot cancel an expired timer")
+
+ timer[2] = True
self.timers = [t for t in self.timers if t != timer]
# For unit testing
@@ -168,11 +174,17 @@ class MockClock(object):
timers = self.timers
self.timers = []
- for time, callback in timers:
+ for t in timers:
+ time, callback, expired = t
+
+ if expired:
+ raise Exception("Timer already expired")
+
if self.now >= time:
+ t[2] = True
callback()
else:
- self.timers.append((time, callback))
+ self.timers.append(t)
class SQLiteMemoryDbPool(ConnectionPool, object):