mirror of
https://mau.dev/maunium/synapse.git
synced 2024-12-14 09:33:53 +01:00
Merge branch 'develop' into pushers
Conflicts: synapse/rest/__init__.py
This commit is contained in:
commit
5c6189ea3e
91 changed files with 638 additions and 1705 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -26,17 +26,12 @@ htmlcov
|
||||||
|
|
||||||
demo/*.db
|
demo/*.db
|
||||||
demo/*.log
|
demo/*.log
|
||||||
|
demo/*.log.*
|
||||||
demo/*.pid
|
demo/*.pid
|
||||||
|
demo/media_store.*
|
||||||
demo/etc
|
demo/etc
|
||||||
|
|
||||||
graph/*.svg
|
|
||||||
graph/*.png
|
|
||||||
graph/*.dot
|
|
||||||
|
|
||||||
**/webclient/config.js
|
|
||||||
**/webclient/test/coverage/
|
|
||||||
**/webclient/test/environment-protractor.js
|
|
||||||
|
|
||||||
uploads
|
uploads
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
media_store/
|
||||||
|
|
16
MANIFEST.in
16
MANIFEST.in
|
@ -1,4 +1,14 @@
|
||||||
recursive-include docs *
|
include synctl
|
||||||
recursive-include tests *.py
|
include LICENSE
|
||||||
|
include VERSION
|
||||||
|
include *.rst
|
||||||
|
include demo/README
|
||||||
|
|
||||||
recursive-include synapse/storage/schema *.sql
|
recursive-include synapse/storage/schema *.sql
|
||||||
recursive-include syweb/webclient *
|
|
||||||
|
recursive-include demo *.dh
|
||||||
|
recursive-include demo *.py
|
||||||
|
recursive-include demo *.sh
|
||||||
|
recursive-include docs *
|
||||||
|
recursive-include scripts *
|
||||||
|
recursive-include tests *.py
|
||||||
|
|
|
@ -52,7 +52,7 @@ resulting conflicts during the upgrade process.
|
||||||
Before running the command the homeserver should be first completely
|
Before running the command the homeserver should be first completely
|
||||||
shutdown. To run it, simply specify the location of the database, e.g.:
|
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
|
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
|
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
|
Before running the command the homeserver should be first completely
|
||||||
shutdown. To run it, simply specify the location of the database, e.g.:
|
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
|
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
|
homeserver. You may notice that the homeserver takes a few seconds longer to
|
||||||
|
|
|
@ -32,7 +32,8 @@ for port in 8080 8081 8082; do
|
||||||
-D --pid-file "$DIR/$port.pid" \
|
-D --pid-file "$DIR/$port.pid" \
|
||||||
--manhole $((port + 1000)) \
|
--manhole $((port + 1000)) \
|
||||||
--tls-dh-params-path "demo/demo.tls.dh" \
|
--tls-dh-params-path "demo/demo.tls.dh" \
|
||||||
$PARAMS $SYNAPSE_PARAMS
|
--media-store-path "demo/media_store.$port" \
|
||||||
|
$PARAMS $SYNAPSE_PARAMS \
|
||||||
|
|
||||||
python -m synapse.app.homeserver \
|
python -m synapse.app.homeserver \
|
||||||
--config-path "demo/etc/$port.config" \
|
--config-path "demo/etc/$port.config" \
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
.loggedin {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
table
|
|
||||||
{
|
|
||||||
border-spacing:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,td
|
|
||||||
{
|
|
||||||
padding:5px;
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<div>
|
|
||||||
<p>This room creation / message sending demo requires a home server to be running on http://localhost:8008</p>
|
|
||||||
</div>
|
|
||||||
<form class="loginForm">
|
|
||||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
|
||||||
<input type="button" class="login" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
<div class="loggedin">
|
|
||||||
<form class="createRoomForm">
|
|
||||||
<input type="text" id="roomAlias" placeholder="Room alias (optional)"></input>
|
|
||||||
<input type="button" class="createRoom" value="Create Room"></input>
|
|
||||||
</form>
|
|
||||||
<form class="sendMessageForm">
|
|
||||||
<input type="text" id="roomId" placeholder="Room ID"></input>
|
|
||||||
<input type="text" id="messageBody" placeholder="Message body"></input>
|
|
||||||
<input type="button" class="sendMessage" value="Send Message"></input>
|
|
||||||
</form>
|
|
||||||
<table id="rooms">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Room ID</th>
|
|
||||||
<th>My state</th>
|
|
||||||
<th>Room Alias</th>
|
|
||||||
<th>Latest message</th>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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<rooms.length; ++i) {
|
|
||||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
|
||||||
addRoom(rooms[i]);
|
|
||||||
}
|
|
||||||
}).fail(function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('.createRoom').live('click', function() {
|
|
||||||
var roomAlias = $("#roomAlias").val();
|
|
||||||
var data = {};
|
|
||||||
if (roomAlias.length > 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 = "<tr>" +
|
|
||||||
"<td>"+data.room_id+"</td>" +
|
|
||||||
"<td>"+data.membership+"</td>" +
|
|
||||||
"<td>"+data.room_alias+"</td>" +
|
|
||||||
"<td>"+data.latest_message+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
$("#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)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
.loggedin {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
table
|
|
||||||
{
|
|
||||||
border-spacing:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,td
|
|
||||||
{
|
|
||||||
padding:5px;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<div>
|
|
||||||
<p>This event stream demo requires a home server to be running on http://localhost:8008</p>
|
|
||||||
</div>
|
|
||||||
<form class="loginForm">
|
|
||||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
|
||||||
<input type="button" class="login" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
<div class="loggedin">
|
|
||||||
<form class="sendMessageForm">
|
|
||||||
<input type="button" class="sendMessage" value="Send random message"></input>
|
|
||||||
</form>
|
|
||||||
<p id="streamErrorText"></p>
|
|
||||||
<table id="rooms">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Room ID</th>
|
|
||||||
<th>Latest message</th>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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<data.chunk.length; ++i) {
|
|
||||||
if (data.chunk[i].type === "m.room.message") {
|
|
||||||
for (var j=0; j<roomInfo.length; ++j) {
|
|
||||||
if (roomInfo[j].room_id === data.chunk[i].room_id) {
|
|
||||||
roomInfo[j].latest_message = data.chunk[i].content.body;
|
|
||||||
hasNewLatestMessage = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNewLatestMessage) {
|
|
||||||
setRooms(roomInfo);
|
|
||||||
}
|
|
||||||
$("#streamErrorText").text("");
|
|
||||||
longpollEventStream();
|
|
||||||
}).fail(function(err) {
|
|
||||||
$("#streamErrorText").text("Event stream error: "+JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
setTimeout(longpollEventStream, 5000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var showLoggedIn = function(data) {
|
|
||||||
accountInfo = data;
|
|
||||||
longpollEventStream();
|
|
||||||
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) {
|
|
||||||
$("#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("");
|
|
||||||
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<rooms.length; ++i) {
|
|
||||||
if ("messages" in rooms[i]) {
|
|
||||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roomInfo = rooms;
|
|
||||||
setRooms(roomInfo);
|
|
||||||
}).fail(function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('.sendMessage').live('click', function() {
|
|
||||||
if (roomInfo.length === 0) {
|
|
||||||
alert("There is no room to send a message to!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = Math.floor(Math.random() * roomInfo.length);
|
|
||||||
|
|
||||||
sendMessage(roomInfo[index].room_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
var sendMessage = function(roomId) {
|
|
||||||
var body = "jsfiddle message @" + $.now();
|
|
||||||
|
|
||||||
if (roomId.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("");
|
|
||||||
},
|
|
||||||
error: function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var setRooms = function(roomList) {
|
|
||||||
// wipe existing entries
|
|
||||||
$("#rooms").find("tr:gt(0)").remove();
|
|
||||||
|
|
||||||
var rows = "";
|
|
||||||
for (var i=0; i<roomList.length; ++i) {
|
|
||||||
row = "<tr>" +
|
|
||||||
"<td>"+roomList[i].room_id+"</td>" +
|
|
||||||
"<td>"+roomList[i].latest_message+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
rows += row;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#rooms").append(rows);
|
|
||||||
};
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -1,56 +0,0 @@
|
||||||
<div class="signUp">
|
|
||||||
<p>Matrix example application: Requires a local home server running at http://localhost:8008</p>
|
|
||||||
<form class="registrationForm">
|
|
||||||
<p>No account? Register:</p>
|
|
||||||
<input type="text" id="userReg" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordReg" placeholder="Password"></input>
|
|
||||||
<input type="button" class="register" value="Register"></input>
|
|
||||||
</form>
|
|
||||||
<form class="loginForm">
|
|
||||||
<p>Got an account? Login:</p>
|
|
||||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
|
||||||
<input type="button" class="login" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="roomListDashboard">
|
|
||||||
<form class="createRoomForm">
|
|
||||||
<input type="text" id="roomAlias" placeholder="Room alias"></input>
|
|
||||||
<input type="button" class="createRoom" value="Create Room"></input>
|
|
||||||
</form>
|
|
||||||
<table id="rooms" class="roomList">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Room</th>
|
|
||||||
<th>My state</th>
|
|
||||||
<th>Latest message</th>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="roomContents">
|
|
||||||
<p id="roomName">Select a room</p>
|
|
||||||
<div class="messageWrapper">
|
|
||||||
<table id="messages">
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<form class="sendMessageForm">
|
|
||||||
<input type="text" class="textEntry" id="body" placeholder="Enter text here..." onkeydown="javascript:if (event.keyCode == 13) document.getElementById('sendMsg').focus()"></input>
|
|
||||||
<input type="button" class="sendMessage" id="sendMsg" value="Send"></input>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p>Member list:</p>
|
|
||||||
<div class="membersWrapper">
|
|
||||||
<table id="members">
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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<data.chunk.length; ++i) {
|
|
||||||
if (data.chunk[i].type === "m.room.message") {
|
|
||||||
console.log("Got new message: " + JSON.stringify(data.chunk[i]));
|
|
||||||
if (viewingRoomId === data.chunk[i].room_id) {
|
|
||||||
addMessage(data.chunk[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j=0; j<roomInfo.length; ++j) {
|
|
||||||
if (roomInfo[j].room_id === data.chunk[i].room_id) {
|
|
||||||
roomInfo[j].latest_message = data.chunk[i].content.body;
|
|
||||||
hasNewLatestMessage = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (data.chunk[i].type === "m.room.member") {
|
|
||||||
if (viewingRoomId === data.chunk[i].room_id) {
|
|
||||||
console.log("Got new member: " + JSON.stringify(data.chunk[i]));
|
|
||||||
addMessage(data.chunk[i]);
|
|
||||||
for (j=0; j<memberInfo.length; ++j) {
|
|
||||||
if (memberInfo[j].state_key === data.chunk[i].state_key) {
|
|
||||||
memberInfo[j] = data.chunk[i];
|
|
||||||
updatedMemberList = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!updatedMemberList) {
|
|
||||||
memberInfo.push(data.chunk[i]);
|
|
||||||
updatedMemberList = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.chunk[i].state_key === accountInfo.user_id) {
|
|
||||||
getCurrentRoomList(); // update our join/invite list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Discarding: " + JSON.stringify(data.chunk[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNewLatestMessage) {
|
|
||||||
setRooms(roomInfo);
|
|
||||||
}
|
|
||||||
if (updatedMemberList) {
|
|
||||||
$("#members").empty();
|
|
||||||
for (i=0; i<memberInfo.length; ++i) {
|
|
||||||
addMember(memberInfo[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
longpollEventStream();
|
|
||||||
}).fail(function(err) {
|
|
||||||
setTimeout(longpollEventStream, 5000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// ************** Registration and Login **************
|
|
||||||
var onLoggedIn = function(data) {
|
|
||||||
accountInfo = data;
|
|
||||||
longpollEventStream();
|
|
||||||
getCurrentRoomList();
|
|
||||||
$(".roomListDashboard").css({visibility: "visible"});
|
|
||||||
$(".roomContents").css({visibility: "visible"});
|
|
||||||
$(".signUp").css({display: "none"});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('.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) {
|
|
||||||
onLoggedIn(data);
|
|
||||||
},
|
|
||||||
error: function(err) {
|
|
||||||
alert("Unable to login: is the home server running?");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.register').live('click', function() {
|
|
||||||
var user = $("#userReg").val();
|
|
||||||
var password = $("#passwordReg").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) {
|
|
||||||
onLoggedIn(data);
|
|
||||||
},
|
|
||||||
error: function(err) {
|
|
||||||
var msg = "Is the home server running?";
|
|
||||||
var errJson = $.parseJSON(err.responseText);
|
|
||||||
if (errJson !== null) {
|
|
||||||
msg = errJson.error;
|
|
||||||
}
|
|
||||||
alert("Unable to register: "+msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ************** Creating a room ******************
|
|
||||||
$('.createRoom').live('click', function() {
|
|
||||||
var roomAlias = $("#roomAlias").val();
|
|
||||||
var data = {};
|
|
||||||
if (roomAlias.length > 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<rooms.length; ++i) {
|
|
||||||
if ("messages" in rooms[i]) {
|
|
||||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roomInfo = rooms;
|
|
||||||
setRooms(roomInfo);
|
|
||||||
}).fail(function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadRoomContent = function(roomId) {
|
|
||||||
console.log("loadRoomContent " + roomId);
|
|
||||||
viewingRoomId = roomId;
|
|
||||||
$("#roomName").text("Room: "+roomId);
|
|
||||||
$(".sendMessageForm").css({visibility: "visible"});
|
|
||||||
getMessages(roomId);
|
|
||||||
getMemberList(roomId);
|
|
||||||
};
|
|
||||||
|
|
||||||
var getMessages = function(roomId) {
|
|
||||||
$("#messages").empty();
|
|
||||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
|
|
||||||
encodeURIComponent(roomId) + "/messages?access_token=" + accountInfo.access_token + "&from=END&dir=b&limit=10";
|
|
||||||
$.getJSON(url, function(data) {
|
|
||||||
for (var i=data.chunk.length-1; 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<data.chunk.length; ++i) {
|
|
||||||
memberInfo.push(data.chunk[i]);
|
|
||||||
addMember(data.chunk[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// ************** Sending messages **************
|
|
||||||
$('.sendMessage').live('click', function() {
|
|
||||||
if (viewingRoomId === undefined) {
|
|
||||||
alert("There is no room to send a message to!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var body = $("#body").val();
|
|
||||||
sendMessage(viewingRoomId, body);
|
|
||||||
});
|
|
||||||
|
|
||||||
var sendMessage = function(roomId, body) {
|
|
||||||
var msgId = $.now();
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$("#body").val("");
|
|
||||||
},
|
|
||||||
error: function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// ************** Navigation and DOM manipulation **************
|
|
||||||
var setRooms = function(roomList) {
|
|
||||||
// wipe existing entries
|
|
||||||
$("#rooms").find("tr:gt(0)").remove();
|
|
||||||
|
|
||||||
var rows = "";
|
|
||||||
for (var i=0; i<roomList.length; ++i) {
|
|
||||||
row = "<tr>" +
|
|
||||||
"<td>"+roomList[i].room_id+"</td>" +
|
|
||||||
"<td>"+roomList[i].membership+"</td>" +
|
|
||||||
"<td>"+roomList[i].latest_message+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
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 = "<em>invited " + data.state_key + " to the room</em>";
|
|
||||||
}
|
|
||||||
else if (data.content.membership === "join") {
|
|
||||||
msg = "<em>joined the room</em>";
|
|
||||||
}
|
|
||||||
else if (data.content.membership === "leave") {
|
|
||||||
msg = "<em>left the room</em>";
|
|
||||||
}
|
|
||||||
else if (data.content.membership === "ban") {
|
|
||||||
msg = "<em>was banned from the room</em>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var row = "<tr>" +
|
|
||||||
"<td>"+data.user_id+"</td>" +
|
|
||||||
"<td>"+msg+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
$("#messages").append(row);
|
|
||||||
};
|
|
||||||
|
|
||||||
var addMember = function(data) {
|
|
||||||
var row = "<tr>" +
|
|
||||||
"<td>"+data.state_key+"</td>" +
|
|
||||||
"<td>"+data.content.membership+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
$("#members").append(row);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
.loggedin {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<div>
|
|
||||||
<p>This registration/login demo requires a home server to be running on http://localhost:8008</p>
|
|
||||||
</div>
|
|
||||||
<form class="registrationForm">
|
|
||||||
<input type="text" id="user" placeholder="Username"></input>
|
|
||||||
<input type="password" id="password" placeholder="Password"></input>
|
|
||||||
<input type="button" class="register" value="Register"></input>
|
|
||||||
</form>
|
|
||||||
<form class="loginForm">
|
|
||||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
|
||||||
<input type="button" class="login" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
<div class="loggedin">
|
|
||||||
<p id="welcomeText"></p>
|
|
||||||
<input type="button" class="testToken" value="Test token"></input>
|
|
||||||
<input type="button" class="logout" value="Logout"></input>
|
|
||||||
<p id="imSyncText"></p>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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)));
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
.loggedin {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
table
|
|
||||||
{
|
|
||||||
border-spacing:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,td
|
|
||||||
{
|
|
||||||
padding:5px;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
<div>
|
|
||||||
<p>This room membership demo requires a home server to be running on http://localhost:8008</p>
|
|
||||||
</div>
|
|
||||||
<form class="loginForm">
|
|
||||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
|
||||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
|
||||||
<input type="button" class="login" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
<div class="loggedin">
|
|
||||||
<form class="createRoomForm">
|
|
||||||
<input type="button" class="createRoom" value="Create Room"></input>
|
|
||||||
</form>
|
|
||||||
<form class="changeMembershipForm">
|
|
||||||
<input type="text" id="roomId" placeholder="Room ID"></input>
|
|
||||||
<input type="text" id="targetUser" placeholder="Target User ID"></input>
|
|
||||||
<select id="membership">
|
|
||||||
<option value="invite">invite</option>
|
|
||||||
<option value="join">join</option>
|
|
||||||
<option value="leave">leave</option>
|
|
||||||
</select>
|
|
||||||
<input type="button" class="changeMembership" value="Change Membership"></input>
|
|
||||||
</form>
|
|
||||||
<form class="joinAliasForm">
|
|
||||||
<input type="text" id="roomAlias" placeholder="Room Alias (#name:domain)"></input>
|
|
||||||
<input type="button" class="joinAlias" value="Join via Alias"></input>
|
|
||||||
</form>
|
|
||||||
<table id="rooms">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Room ID</th>
|
|
||||||
<th>My state</th>
|
|
||||||
<th>Room Alias</th>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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<rooms.length; ++i) {
|
|
||||||
addRoom(rooms[i]);
|
|
||||||
}
|
|
||||||
}).fail(function(err) {
|
|
||||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('.createRoom').live('click', function() {
|
|
||||||
var data = {};
|
|
||||||
$.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 = "<tr>" +
|
|
||||||
"<td>"+data.room_id+"</td>" +
|
|
||||||
"<td>"+data.membership+"</td>" +
|
|
||||||
"<td>"+data.room_alias+"</td>" +
|
|
||||||
"</tr>";
|
|
||||||
$("#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)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -8,3 +8,11 @@ test = trial
|
||||||
|
|
||||||
[trial]
|
[trial]
|
||||||
test_suite = tests
|
test_suite = tests
|
||||||
|
|
||||||
|
[check-manifest]
|
||||||
|
ignore =
|
||||||
|
contrib
|
||||||
|
contrib/*
|
||||||
|
docs/*
|
||||||
|
pylint.cfg
|
||||||
|
tox.ini
|
||||||
|
|
|
@ -26,8 +26,8 @@ from twisted.web.resource import Resource
|
||||||
from twisted.web.static import File
|
from twisted.web.static import File
|
||||||
from twisted.web.server import Site
|
from twisted.web.server import Site
|
||||||
from synapse.http.server import JsonResource, RootRedirect
|
from synapse.http.server import JsonResource, RootRedirect
|
||||||
from synapse.media.v0.content_repository import ContentRepoResource
|
from synapse.rest.media.v0.content_repository import ContentRepoResource
|
||||||
from synapse.media.v1.media_repository import MediaRepositoryResource
|
from synapse.rest.media.v1.media_repository import MediaRepositoryResource
|
||||||
from synapse.http.server_key_resource import LocalKey
|
from synapse.http.server_key_resource import LocalKey
|
||||||
from synapse.http.matrixfederationclient import MatrixFederationHttpClient
|
from synapse.http.matrixfederationclient import MatrixFederationHttpClient
|
||||||
from synapse.api.urls import (
|
from synapse.api.urls import (
|
||||||
|
@ -241,13 +241,20 @@ def setup():
|
||||||
except UpgradeDatabaseException:
|
except UpgradeDatabaseException:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"\nFailed to upgrade database.\n"
|
"\nFailed to upgrade database.\n"
|
||||||
"Have you checked for version specific instructions in UPGRADES.rst?\n"
|
"Have you checked for version specific instructions in"
|
||||||
|
" UPGRADES.rst?\n"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
logger.info("Database prepared in %s.", db_name)
|
logger.info("Database prepared in %s.", db_name)
|
||||||
|
|
||||||
hs.get_db_pool()
|
db_pool = hs.get_db_pool()
|
||||||
|
|
||||||
|
if db_name == ":memory:":
|
||||||
|
# Memory databases will need to be setup each time they are opened.
|
||||||
|
reactor.callWhenRunning(
|
||||||
|
db_pool.runWithConnection, prepare_database
|
||||||
|
)
|
||||||
|
|
||||||
if config.manhole:
|
if config.manhole:
|
||||||
f = twisted.manhole.telnet.ShellFactory()
|
f = twisted.manhole.telnet.ShellFactory()
|
||||||
|
|
|
@ -20,6 +20,9 @@ import os
|
||||||
class DatabaseConfig(Config):
|
class DatabaseConfig(Config):
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
super(DatabaseConfig, self).__init__(args)
|
super(DatabaseConfig, self).__init__(args)
|
||||||
|
if args.database_path == ":memory:":
|
||||||
|
self.database_path = ":memory:"
|
||||||
|
else:
|
||||||
self.database_path = self.abspath(args.database_path)
|
self.database_path = self.abspath(args.database_path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -33,12 +33,6 @@ class EventBuilder(EventBase):
|
||||||
unsigned=unsigned
|
unsigned=unsigned
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_event_key(self, key, value):
|
|
||||||
self._event_dict[key] = value
|
|
||||||
|
|
||||||
def update_event_keys(self, other_dict):
|
|
||||||
self._event_dict.update(other_dict)
|
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return FrozenEvent.from_event(self)
|
return FrozenEvent.from_event(self)
|
||||||
|
|
||||||
|
|
|
@ -256,23 +256,21 @@ class ReplicationLayer(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def get_state_for_context(self, destination, context, event_id):
|
def get_state_for_room(self, destination, room_id, event_id):
|
||||||
"""Requests all of the `current` state PDUs for a given context from
|
"""Requests all of the `current` state PDUs for a given room from
|
||||||
a remote home server.
|
a remote home server.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
destination (str): The remote homeserver to query for the state.
|
destination (str): The remote homeserver to query for the state.
|
||||||
context (str): The context we're interested in.
|
room_id (str): The id of the room we're interested in.
|
||||||
event_id (str): The id of the event we want the state at.
|
event_id (str): The id of the event we want the state at.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred: Results in a list of PDUs.
|
Deferred: Results in a list of PDUs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = yield self.transport_layer.get_context_state(
|
result = yield self.transport_layer.get_room_state(
|
||||||
destination,
|
destination, room_id, event_id=event_id,
|
||||||
context,
|
|
||||||
event_id=event_id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
pdus = [
|
pdus = [
|
||||||
|
@ -288,9 +286,9 @@ class ReplicationLayer(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def get_event_auth(self, destination, context, event_id):
|
def get_event_auth(self, destination, room_id, event_id):
|
||||||
res = yield self.transport_layer.get_event_auth(
|
res = yield self.transport_layer.get_event_auth(
|
||||||
destination, context, event_id,
|
destination, room_id, event_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
auth_chain = [
|
auth_chain = [
|
||||||
|
@ -304,9 +302,9 @@ class ReplicationLayer(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def on_backfill_request(self, origin, context, versions, limit):
|
def on_backfill_request(self, origin, room_id, versions, limit):
|
||||||
pdus = yield self.handler.on_backfill_request(
|
pdus = yield self.handler.on_backfill_request(
|
||||||
origin, context, versions, limit
|
origin, room_id, versions, limit
|
||||||
)
|
)
|
||||||
|
|
||||||
defer.returnValue((200, self._transaction_from_pdus(pdus).get_dict()))
|
defer.returnValue((200, self._transaction_from_pdus(pdus).get_dict()))
|
||||||
|
@ -380,12 +378,10 @@ class ReplicationLayer(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def on_context_state_request(self, origin, context, event_id):
|
def on_context_state_request(self, origin, room_id, event_id):
|
||||||
if event_id:
|
if event_id:
|
||||||
pdus = yield self.handler.get_state_for_pdu(
|
pdus = yield self.handler.get_state_for_pdu(
|
||||||
origin,
|
origin, room_id, event_id,
|
||||||
context,
|
|
||||||
event_id,
|
|
||||||
)
|
)
|
||||||
auth_chain = yield self.store.get_auth_chain(
|
auth_chain = yield self.store.get_auth_chain(
|
||||||
[pdu.event_id for pdu in pdus]
|
[pdu.event_id for pdu in pdus]
|
||||||
|
@ -413,7 +409,7 @@ class ReplicationLayer(object):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def on_pull_request(self, origin, versions):
|
def on_pull_request(self, origin, versions):
|
||||||
raise NotImplementedError("Pull transacions not implemented")
|
raise NotImplementedError("Pull transactions not implemented")
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_query_request(self, query_type, args):
|
def on_query_request(self, query_type, args):
|
||||||
|
@ -422,30 +418,21 @@ class ReplicationLayer(object):
|
||||||
defer.returnValue((200, response))
|
defer.returnValue((200, response))
|
||||||
else:
|
else:
|
||||||
defer.returnValue(
|
defer.returnValue(
|
||||||
(404, "No handler for Query type '%s'" % (query_type, ))
|
(404, "No handler for Query type '%s'" % (query_type,))
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_make_join_request(self, context, user_id):
|
def on_make_join_request(self, room_id, user_id):
|
||||||
pdu = yield self.handler.on_make_join_request(context, user_id)
|
pdu = yield self.handler.on_make_join_request(room_id, user_id)
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
defer.returnValue({
|
defer.returnValue({"event": pdu.get_pdu_json(time_now)})
|
||||||
"event": pdu.get_pdu_json(time_now),
|
|
||||||
})
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_invite_request(self, origin, content):
|
def on_invite_request(self, origin, content):
|
||||||
pdu = self.event_from_pdu_json(content)
|
pdu = self.event_from_pdu_json(content)
|
||||||
ret_pdu = yield self.handler.on_invite_request(origin, pdu)
|
ret_pdu = yield self.handler.on_invite_request(origin, pdu)
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
defer.returnValue(
|
defer.returnValue((200, {"event": ret_pdu.get_pdu_json(time_now)}))
|
||||||
(
|
|
||||||
200,
|
|
||||||
{
|
|
||||||
"event": ret_pdu.get_pdu_json(time_now),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_send_join_request(self, origin, content):
|
def on_send_join_request(self, origin, content):
|
||||||
|
@ -462,26 +449,17 @@ class ReplicationLayer(object):
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_event_auth(self, origin, context, event_id):
|
def on_event_auth(self, origin, room_id, event_id):
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
auth_pdus = yield self.handler.on_event_auth(event_id)
|
auth_pdus = yield self.handler.on_event_auth(event_id)
|
||||||
defer.returnValue(
|
defer.returnValue((200, {
|
||||||
(
|
"auth_chain": [a.get_pdu_json(time_now) for a in auth_pdus],
|
||||||
200,
|
}))
|
||||||
{
|
|
||||||
"auth_chain": [
|
|
||||||
a.get_pdu_json(time_now) for a in auth_pdus
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def make_join(self, destination, context, user_id):
|
def make_join(self, destination, room_id, user_id):
|
||||||
ret = yield self.transport_layer.make_join(
|
ret = yield self.transport_layer.make_join(
|
||||||
destination=destination,
|
destination, room_id, user_id
|
||||||
context=context,
|
|
||||||
user_id=user_id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
pdu_dict = ret["event"]
|
pdu_dict = ret["event"]
|
||||||
|
@ -494,10 +472,10 @@ class ReplicationLayer(object):
|
||||||
def send_join(self, destination, pdu):
|
def send_join(self, destination, pdu):
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
_, content = yield self.transport_layer.send_join(
|
_, content = yield self.transport_layer.send_join(
|
||||||
destination,
|
destination=destination,
|
||||||
pdu.room_id,
|
room_id=pdu.room_id,
|
||||||
pdu.event_id,
|
event_id=pdu.event_id,
|
||||||
pdu.get_pdu_json(time_now),
|
content=pdu.get_pdu_json(time_now),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("Got content: %s", content)
|
logger.debug("Got content: %s", content)
|
||||||
|
@ -507,9 +485,6 @@ class ReplicationLayer(object):
|
||||||
for p in content.get("state", [])
|
for p in content.get("state", [])
|
||||||
]
|
]
|
||||||
|
|
||||||
# FIXME: We probably want to do something with the auth_chain given
|
|
||||||
# to us
|
|
||||||
|
|
||||||
auth_chain = [
|
auth_chain = [
|
||||||
self.event_from_pdu_json(p, outlier=True)
|
self.event_from_pdu_json(p, outlier=True)
|
||||||
for p in content.get("auth_chain", [])
|
for p in content.get("auth_chain", [])
|
||||||
|
@ -523,11 +498,11 @@ class ReplicationLayer(object):
|
||||||
})
|
})
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def send_invite(self, destination, context, event_id, pdu):
|
def send_invite(self, destination, room_id, event_id, pdu):
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
code, content = yield self.transport_layer.send_invite(
|
code, content = yield self.transport_layer.send_invite(
|
||||||
destination=destination,
|
destination=destination,
|
||||||
context=context,
|
room_id=room_id,
|
||||||
event_id=event_id,
|
event_id=event_id,
|
||||||
content=pdu.get_pdu_json(time_now),
|
content=pdu.get_pdu_json(time_now),
|
||||||
)
|
)
|
||||||
|
@ -657,7 +632,7 @@ class ReplicationLayer(object):
|
||||||
"_handle_new_pdu getting state for %s",
|
"_handle_new_pdu getting state for %s",
|
||||||
pdu.room_id
|
pdu.room_id
|
||||||
)
|
)
|
||||||
state, auth_chain = yield self.get_state_for_context(
|
state, auth_chain = yield self.get_state_for_room(
|
||||||
origin, pdu.room_id, pdu.event_id,
|
origin, pdu.room_id, pdu.event_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -830,14 +805,15 @@ class _TransactionQueue(object):
|
||||||
pending_failures = self.pending_failures_by_dest.pop(destination, [])
|
pending_failures = self.pending_failures_by_dest.pop(destination, [])
|
||||||
|
|
||||||
if pending_pdus:
|
if pending_pdus:
|
||||||
logger.info("TX [%s] len(pending_pdus_by_dest[dest]) = %d", destination, len(pending_pdus))
|
logger.info("TX [%s] len(pending_pdus_by_dest[dest]) = %d",
|
||||||
|
destination, len(pending_pdus))
|
||||||
|
|
||||||
if not pending_pdus and not pending_edus and not pending_failures:
|
if not pending_pdus and not pending_edus and not pending_failures:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"TX [%s] Attempting new transaction "
|
"TX [%s] Attempting new transaction"
|
||||||
"(pdus: %d, edus: %d, failures: %d)",
|
" (pdus: %d, edus: %d, failures: %d)",
|
||||||
destination,
|
destination,
|
||||||
len(pending_pdus),
|
len(pending_pdus),
|
||||||
len(pending_edus),
|
len(pending_edus),
|
||||||
|
|
62
synapse/federation/transport/__init__.py
Normal file
62
synapse/federation/transport/__init__.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2014, 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.
|
||||||
|
|
||||||
|
"""The transport layer is responsible for both sending transactions to remote
|
||||||
|
home servers and receiving a variety of requests from other home servers.
|
||||||
|
|
||||||
|
By default this is done over HTTPS (and all home servers are required to
|
||||||
|
support HTTPS), however individual pairings of servers may decide to
|
||||||
|
communicate over a different (albeit still reliable) protocol.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .server import TransportLayerServer
|
||||||
|
from .client import TransportLayerClient
|
||||||
|
|
||||||
|
|
||||||
|
class TransportLayer(TransportLayerServer, TransportLayerClient):
|
||||||
|
"""This is a basic implementation of the transport layer that translates
|
||||||
|
transactions and other requests to/from HTTP.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
server_name (str): Local home server host
|
||||||
|
|
||||||
|
server (synapse.http.server.HttpServer): the http server to
|
||||||
|
register listeners on
|
||||||
|
|
||||||
|
client (synapse.http.client.HttpClient): the http client used to
|
||||||
|
send requests
|
||||||
|
|
||||||
|
request_handler (TransportRequestHandler): The handler to fire when we
|
||||||
|
receive requests for data.
|
||||||
|
|
||||||
|
received_handler (TransportReceivedHandler): The handler to fire when
|
||||||
|
we receive data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, homeserver, server_name, server, client):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
server_name (str): Local home server host
|
||||||
|
server (synapse.protocol.http.HttpServer): the http server to
|
||||||
|
register listeners on
|
||||||
|
client (synapse.protocol.http.HttpClient): the http client used to
|
||||||
|
send requests
|
||||||
|
"""
|
||||||
|
self.keyring = homeserver.get_keyring()
|
||||||
|
self.server_name = server_name
|
||||||
|
self.server = server
|
||||||
|
self.client = client
|
||||||
|
self.request_handler = None
|
||||||
|
self.received_handler = None
|
215
synapse/federation/transport/client.py
Normal file
215
synapse/federation/transport/client.py
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2014, 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.
|
||||||
|
|
||||||
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
from synapse.api.urls import FEDERATION_PREFIX as PREFIX
|
||||||
|
from synapse.util.logutils import log_function
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TransportLayerClient(object):
|
||||||
|
"""Sends federation HTTP requests to other servers"""
|
||||||
|
|
||||||
|
@log_function
|
||||||
|
def get_room_state(self, destination, room_id, event_id):
|
||||||
|
""" Requests all state for a given room from the given server at the
|
||||||
|
given event.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
destination (str): The host name of the remote home server we want
|
||||||
|
to get the state from.
|
||||||
|
context (str): The name of the context we want the state of
|
||||||
|
event_id (str): The event we want the context at.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred: Results in a dict received from the remote homeserver.
|
||||||
|
"""
|
||||||
|
logger.debug("get_room_state dest=%s, room=%s",
|
||||||
|
destination, room_id)
|
||||||
|
|
||||||
|
path = PREFIX + "/state/%s/" % room_id
|
||||||
|
return self.client.get_json(
|
||||||
|
destination, path=path, args={"event_id": event_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
@log_function
|
||||||
|
def get_event(self, destination, event_id):
|
||||||
|
""" Requests the pdu with give id and origin from the given server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
destination (str): The host name of the remote home server we want
|
||||||
|
to get the state from.
|
||||||
|
event_id (str): The id of the event being requested.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred: Results in a dict received from the remote homeserver.
|
||||||
|
"""
|
||||||
|
logger.debug("get_pdu dest=%s, event_id=%s",
|
||||||
|
destination, event_id)
|
||||||
|
|
||||||
|
path = PREFIX + "/event/%s/" % (event_id, )
|
||||||
|
return self.client.get_json(destination, path=path)
|
||||||
|
|
||||||
|
@log_function
|
||||||
|
def backfill(self, destination, room_id, event_tuples, limit):
|
||||||
|
""" Requests `limit` previous PDUs in a given context before list of
|
||||||
|
PDUs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dest (str)
|
||||||
|
room_id (str)
|
||||||
|
event_tuples (list)
|
||||||
|
limt (int)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred: Results in a dict received from the remote homeserver.
|
||||||
|
"""
|
||||||
|
logger.debug(
|
||||||
|
"backfill dest=%s, room_id=%s, event_tuples=%s, limit=%s",
|
||||||
|
destination, room_id, repr(event_tuples), str(limit)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not event_tuples:
|
||||||
|
# TODO: raise?
|
||||||
|
return
|
||||||
|
|
||||||
|
path = PREFIX + "/backfill/%s/" % (room_id,)
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"v": event_tuples,
|
||||||
|
"limit": [str(limit)],
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.client.get_json(
|
||||||
|
destination,
|
||||||
|
path=path,
|
||||||
|
args=args,
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def send_transaction(self, transaction, json_data_callback=None):
|
||||||
|
""" Sends the given Transaction to its destination
|
||||||
|
|
||||||
|
Args:
|
||||||
|
transaction (Transaction)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred: Results of the deferred is a tuple in the form of
|
||||||
|
(response_code, response_body) where the response_body is a
|
||||||
|
python dict decoded from json
|
||||||
|
"""
|
||||||
|
logger.debug(
|
||||||
|
"send_data dest=%s, txid=%s",
|
||||||
|
transaction.destination, transaction.transaction_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if transaction.destination == self.server_name:
|
||||||
|
raise RuntimeError("Transport layer cannot send to itself!")
|
||||||
|
|
||||||
|
# FIXME: This is only used by the tests. The actual json sent is
|
||||||
|
# generated by the json_data_callback.
|
||||||
|
json_data = transaction.get_dict()
|
||||||
|
|
||||||
|
code, response = yield self.client.put_json(
|
||||||
|
transaction.destination,
|
||||||
|
path=PREFIX + "/send/%s/" % transaction.transaction_id,
|
||||||
|
data=json_data,
|
||||||
|
json_data_callback=json_data_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"send_data dest=%s, txid=%s, got response: %d",
|
||||||
|
transaction.destination, transaction.transaction_id, code
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue((code, response))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def make_query(self, destination, query_type, args, retry_on_dns_fail):
|
||||||
|
path = PREFIX + "/query/%s" % query_type
|
||||||
|
|
||||||
|
response = yield self.client.get_json(
|
||||||
|
destination=destination,
|
||||||
|
path=path,
|
||||||
|
args=args,
|
||||||
|
retry_on_dns_fail=retry_on_dns_fail,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def make_join(self, destination, room_id, user_id, retry_on_dns_fail=True):
|
||||||
|
path = PREFIX + "/make_join/%s/%s" % (room_id, user_id)
|
||||||
|
|
||||||
|
response = yield self.client.get_json(
|
||||||
|
destination=destination,
|
||||||
|
path=path,
|
||||||
|
retry_on_dns_fail=retry_on_dns_fail,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def send_join(self, destination, room_id, event_id, content):
|
||||||
|
path = PREFIX + "/send_join/%s/%s" % (room_id, event_id)
|
||||||
|
|
||||||
|
code, content = yield self.client.put_json(
|
||||||
|
destination=destination,
|
||||||
|
path=path,
|
||||||
|
data=content,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not 200 <= code < 300:
|
||||||
|
raise RuntimeError("Got %d from send_join", code)
|
||||||
|
|
||||||
|
defer.returnValue(json.loads(content))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def send_invite(self, destination, room_id, event_id, content):
|
||||||
|
path = PREFIX + "/invite/%s/%s" % (room_id, event_id)
|
||||||
|
|
||||||
|
code, content = yield self.client.put_json(
|
||||||
|
destination=destination,
|
||||||
|
path=path,
|
||||||
|
data=content,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not 200 <= code < 300:
|
||||||
|
raise RuntimeError("Got %d from send_invite", code)
|
||||||
|
|
||||||
|
defer.returnValue(json.loads(content))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def get_event_auth(self, destination, room_id, event_id):
|
||||||
|
path = PREFIX + "/event_auth/%s/%s" % (room_id, event_id)
|
||||||
|
|
||||||
|
response = yield self.client.get_json(
|
||||||
|
destination=destination,
|
||||||
|
path=path,
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(response)
|
|
@ -13,14 +13,6 @@
|
||||||
# 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.
|
||||||
|
|
||||||
"""The transport layer is responsible for both sending transactions to remote
|
|
||||||
home servers and receiving a variety of requests from other home servers.
|
|
||||||
|
|
||||||
Typically, this is done over HTTP (and all home servers are required to
|
|
||||||
support HTTP), however individual pairings of servers may decide to communicate
|
|
||||||
over a different (albeit still reliable) protocol.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.urls import FEDERATION_PREFIX as PREFIX
|
from synapse.api.urls import FEDERATION_PREFIX as PREFIX
|
||||||
|
@ -35,241 +27,8 @@ import re
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TransportLayer(object):
|
class TransportLayerServer(object):
|
||||||
"""This is a basic implementation of the transport layer that translates
|
"""Handles incoming federation HTTP requests"""
|
||||||
transactions and other requests to/from HTTP.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
server_name (str): Local home server host
|
|
||||||
|
|
||||||
server (synapse.http.server.HttpServer): the http server to
|
|
||||||
register listeners on
|
|
||||||
|
|
||||||
client (synapse.http.client.HttpClient): the http client used to
|
|
||||||
send requests
|
|
||||||
|
|
||||||
request_handler (TransportRequestHandler): The handler to fire when we
|
|
||||||
receive requests for data.
|
|
||||||
|
|
||||||
received_handler (TransportReceivedHandler): The handler to fire when
|
|
||||||
we receive data.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, homeserver, server_name, server, client):
|
|
||||||
"""
|
|
||||||
Args:
|
|
||||||
server_name (str): Local home server host
|
|
||||||
server (synapse.protocol.http.HttpServer): the http server to
|
|
||||||
register listeners on
|
|
||||||
client (synapse.protocol.http.HttpClient): the http client used to
|
|
||||||
send requests
|
|
||||||
"""
|
|
||||||
self.keyring = homeserver.get_keyring()
|
|
||||||
self.server_name = server_name
|
|
||||||
self.server = server
|
|
||||||
self.client = client
|
|
||||||
self.request_handler = None
|
|
||||||
self.received_handler = None
|
|
||||||
|
|
||||||
@log_function
|
|
||||||
def get_context_state(self, destination, context, event_id=None):
|
|
||||||
""" Requests all state for a given context (i.e. room) from the
|
|
||||||
given server.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (str): The host name of the remote home server we want
|
|
||||||
to get the state from.
|
|
||||||
context (str): The name of the context we want the state of
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred: Results in a dict received from the remote homeserver.
|
|
||||||
"""
|
|
||||||
logger.debug("get_context_state dest=%s, context=%s",
|
|
||||||
destination, context)
|
|
||||||
|
|
||||||
subpath = "/state/%s/" % context
|
|
||||||
|
|
||||||
args = {}
|
|
||||||
if event_id:
|
|
||||||
args["event_id"] = event_id
|
|
||||||
|
|
||||||
return self._do_request_for_transaction(
|
|
||||||
destination, subpath, args=args
|
|
||||||
)
|
|
||||||
|
|
||||||
@log_function
|
|
||||||
def get_event(self, destination, event_id):
|
|
||||||
""" Requests the pdu with give id and origin from the given server.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (str): The host name of the remote home server we want
|
|
||||||
to get the state from.
|
|
||||||
event_id (str): The id of the event being requested.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred: Results in a dict received from the remote homeserver.
|
|
||||||
"""
|
|
||||||
logger.debug("get_pdu dest=%s, event_id=%s",
|
|
||||||
destination, event_id)
|
|
||||||
|
|
||||||
subpath = "/event/%s/" % (event_id, )
|
|
||||||
|
|
||||||
return self._do_request_for_transaction(destination, subpath)
|
|
||||||
|
|
||||||
@log_function
|
|
||||||
def backfill(self, dest, context, event_tuples, limit):
|
|
||||||
""" Requests `limit` previous PDUs in a given context before list of
|
|
||||||
PDUs.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
dest (str)
|
|
||||||
context (str)
|
|
||||||
event_tuples (list)
|
|
||||||
limt (int)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred: Results in a dict received from the remote homeserver.
|
|
||||||
"""
|
|
||||||
logger.debug(
|
|
||||||
"backfill dest=%s, context=%s, event_tuples=%s, limit=%s",
|
|
||||||
dest, context, repr(event_tuples), str(limit)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not event_tuples:
|
|
||||||
# TODO: raise?
|
|
||||||
return
|
|
||||||
|
|
||||||
subpath = "/backfill/%s/" % (context,)
|
|
||||||
|
|
||||||
args = {
|
|
||||||
"v": event_tuples,
|
|
||||||
"limit": [str(limit)],
|
|
||||||
}
|
|
||||||
|
|
||||||
return self._do_request_for_transaction(
|
|
||||||
dest,
|
|
||||||
subpath,
|
|
||||||
args=args,
|
|
||||||
)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def send_transaction(self, transaction, json_data_callback=None):
|
|
||||||
""" Sends the given Transaction to its destination
|
|
||||||
|
|
||||||
Args:
|
|
||||||
transaction (Transaction)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred: Results of the deferred is a tuple in the form of
|
|
||||||
(response_code, response_body) where the response_body is a
|
|
||||||
python dict decoded from json
|
|
||||||
"""
|
|
||||||
logger.debug(
|
|
||||||
"send_data dest=%s, txid=%s",
|
|
||||||
transaction.destination, transaction.transaction_id
|
|
||||||
)
|
|
||||||
|
|
||||||
if transaction.destination == self.server_name:
|
|
||||||
raise RuntimeError("Transport layer cannot send to itself!")
|
|
||||||
|
|
||||||
# FIXME: This is only used by the tests. The actual json sent is
|
|
||||||
# generated by the json_data_callback.
|
|
||||||
json_data = transaction.get_dict()
|
|
||||||
|
|
||||||
code, response = yield self.client.put_json(
|
|
||||||
transaction.destination,
|
|
||||||
path=PREFIX + "/send/%s/" % transaction.transaction_id,
|
|
||||||
data=json_data,
|
|
||||||
json_data_callback=json_data_callback,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
"send_data dest=%s, txid=%s, got response: %d",
|
|
||||||
transaction.destination, transaction.transaction_id, code
|
|
||||||
)
|
|
||||||
|
|
||||||
defer.returnValue((code, response))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def make_query(self, destination, query_type, args, retry_on_dns_fail):
|
|
||||||
path = PREFIX + "/query/%s" % query_type
|
|
||||||
|
|
||||||
response = yield self.client.get_json(
|
|
||||||
destination=destination,
|
|
||||||
path=path,
|
|
||||||
args=args,
|
|
||||||
retry_on_dns_fail=retry_on_dns_fail,
|
|
||||||
)
|
|
||||||
|
|
||||||
defer.returnValue(response)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def make_join(self, destination, context, user_id, retry_on_dns_fail=True):
|
|
||||||
path = PREFIX + "/make_join/%s/%s" % (context, user_id,)
|
|
||||||
|
|
||||||
response = yield self.client.get_json(
|
|
||||||
destination=destination,
|
|
||||||
path=path,
|
|
||||||
retry_on_dns_fail=retry_on_dns_fail,
|
|
||||||
)
|
|
||||||
|
|
||||||
defer.returnValue(response)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def send_join(self, destination, context, event_id, content):
|
|
||||||
path = PREFIX + "/send_join/%s/%s" % (
|
|
||||||
context,
|
|
||||||
event_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
code, content = yield self.client.put_json(
|
|
||||||
destination=destination,
|
|
||||||
path=path,
|
|
||||||
data=content,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not 200 <= code < 300:
|
|
||||||
raise RuntimeError("Got %d from send_join", code)
|
|
||||||
|
|
||||||
defer.returnValue(json.loads(content))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def send_invite(self, destination, context, event_id, content):
|
|
||||||
path = PREFIX + "/invite/%s/%s" % (
|
|
||||||
context,
|
|
||||||
event_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
code, content = yield self.client.put_json(
|
|
||||||
destination=destination,
|
|
||||||
path=path,
|
|
||||||
data=content,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not 200 <= code < 300:
|
|
||||||
raise RuntimeError("Got %d from send_invite", code)
|
|
||||||
|
|
||||||
defer.returnValue(json.loads(content))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def get_event_auth(self, destination, context, event_id):
|
|
||||||
path = PREFIX + "/event_auth/%s/%s" % (
|
|
||||||
context,
|
|
||||||
event_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
response = yield self.client.get_json(
|
|
||||||
destination=destination,
|
|
||||||
path=path,
|
|
||||||
)
|
|
||||||
|
|
||||||
defer.returnValue(response)
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _authenticate_request(self, request):
|
def _authenticate_request(self, request):
|
||||||
|
@ -373,8 +132,6 @@ class TransportLayer(object):
|
||||||
"""
|
"""
|
||||||
self.request_handler = handler
|
self.request_handler = handler
|
||||||
|
|
||||||
# TODO(markjh): Namespace the federation URI paths
|
|
||||||
|
|
||||||
# This is for when someone asks us for everything since version X
|
# This is for when someone asks us for everything since version X
|
||||||
self.server.register_path(
|
self.server.register_path(
|
||||||
"GET",
|
"GET",
|
||||||
|
@ -528,34 +285,6 @@ class TransportLayer(object):
|
||||||
|
|
||||||
defer.returnValue((code, response))
|
defer.returnValue((code, response))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
@log_function
|
|
||||||
def _do_request_for_transaction(self, destination, subpath, args={}):
|
|
||||||
"""
|
|
||||||
Args:
|
|
||||||
destination (str)
|
|
||||||
path (str)
|
|
||||||
args (dict): This is parsed directly to the HttpClient.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred: Results in a dict.
|
|
||||||
"""
|
|
||||||
|
|
||||||
data = yield self.client.get_json(
|
|
||||||
destination,
|
|
||||||
path=PREFIX + subpath,
|
|
||||||
args=args,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add certain keys to the JSON, ready for decoding as a Transaction
|
|
||||||
data.update(
|
|
||||||
origin=destination,
|
|
||||||
destination=self.server_name,
|
|
||||||
transaction_id=None
|
|
||||||
)
|
|
||||||
|
|
||||||
defer.returnValue(data)
|
|
||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
def _on_backfill_request(self, origin, context, v_list, limits):
|
def _on_backfill_request(self, origin, context, v_list, limits):
|
||||||
if not limits:
|
if not limits:
|
|
@ -144,7 +144,5 @@ class BaseHandler(object):
|
||||||
yield self.notifier.on_new_room_event(event, extra_users=extra_users)
|
yield self.notifier.on_new_room_event(event, extra_users=extra_users)
|
||||||
|
|
||||||
yield federation_handler.handle_new_event(
|
yield federation_handler.handle_new_event(
|
||||||
event,
|
event, destinations=destinations,
|
||||||
None,
|
|
||||||
destinations=destinations,
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -70,10 +70,8 @@ class EventStreamHandler(BaseHandler):
|
||||||
pagin_config.from_token = None
|
pagin_config.from_token = None
|
||||||
|
|
||||||
rm_handler = self.hs.get_handlers().room_member_handler
|
rm_handler = self.hs.get_handlers().room_member_handler
|
||||||
logger.debug("BETA")
|
|
||||||
room_ids = yield rm_handler.get_rooms_for_user(auth_user)
|
room_ids = yield rm_handler.get_rooms_for_user(auth_user)
|
||||||
|
|
||||||
logger.debug("ALPHA")
|
|
||||||
with PreserveLoggingContext():
|
with PreserveLoggingContext():
|
||||||
events, tokens = yield self.notifier.get_events_for(
|
events, tokens = yield self.notifier.get_events_for(
|
||||||
auth_user, room_ids, pagin_config, timeout
|
auth_user, room_ids, pagin_config, timeout
|
||||||
|
|
|
@ -75,14 +75,14 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def handle_new_event(self, event, snapshot, destinations):
|
def handle_new_event(self, event, destinations):
|
||||||
""" Takes in an event from the client to server side, that has already
|
""" Takes in an event from the client to server side, that has already
|
||||||
been authed and handled by the state module, and sends it to any
|
been authed and handled by the state module, and sends it to any
|
||||||
remote home servers that may be interested.
|
remote home servers that may be interested.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event
|
event: The event to send
|
||||||
snapshot (.storage.Snapshot): THe snapshot the event happened after
|
destinations: A list of destinations to send it to
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred: Resolved when it has successfully been queued for
|
Deferred: Resolved when it has successfully been queued for
|
||||||
|
@ -154,7 +154,7 @@ class FederationHandler(BaseHandler):
|
||||||
replication = self.replication_layer
|
replication = self.replication_layer
|
||||||
|
|
||||||
if not state:
|
if not state:
|
||||||
state, auth_chain = yield replication.get_state_for_context(
|
state, auth_chain = yield replication.get_state_for_room(
|
||||||
origin, context=event.room_id, event_id=event.event_id,
|
origin, context=event.room_id, event_id=event.event_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ class FederationHandler(BaseHandler):
|
||||||
"""
|
"""
|
||||||
pdu = yield self.replication_layer.send_invite(
|
pdu = yield self.replication_layer.send_invite(
|
||||||
destination=target_host,
|
destination=target_host,
|
||||||
context=event.room_id,
|
room_id=event.room_id,
|
||||||
event_id=event.event_id,
|
event_id=event.event_id,
|
||||||
pdu=event
|
pdu=event
|
||||||
)
|
)
|
||||||
|
|
|
@ -120,6 +120,10 @@ class TypingNotificationHandler(BaseHandler):
|
||||||
|
|
||||||
member = RoomMember(room_id=room_id, user=target_user)
|
member = RoomMember(room_id=room_id, user=target_user)
|
||||||
|
|
||||||
|
if member in self._member_typing_timer:
|
||||||
|
self.clock.cancel_call_later(self._member_typing_timer[member])
|
||||||
|
del self._member_typing_timer[member]
|
||||||
|
|
||||||
yield self._stopped_typing(member)
|
yield self._stopped_typing(member)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -142,7 +146,9 @@ class TypingNotificationHandler(BaseHandler):
|
||||||
|
|
||||||
del self._member_typing_until[member]
|
del self._member_typing_until[member]
|
||||||
|
|
||||||
self.clock.cancel_call_later(self._member_typing_timer[member])
|
if member in self._member_typing_timer:
|
||||||
|
# Don't cancel it - either it already expired, or the real
|
||||||
|
# stopped_typing() will cancel it
|
||||||
del self._member_typing_timer[member]
|
del self._member_typing_timer[member]
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -72,7 +72,6 @@ class MatrixFederationHttpClient(object):
|
||||||
requests.
|
requests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
self.signing_key = hs.config.signing_key[0]
|
self.signing_key = hs.config.signing_key[0]
|
||||||
|
|
|
@ -244,14 +244,14 @@ class Notifier(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
if timeout:
|
if timeout:
|
||||||
self.clock.call_later(timeout/1000.0, _timeout_listener)
|
|
||||||
|
|
||||||
self._register_with_keys(listener)
|
self._register_with_keys(listener)
|
||||||
|
|
||||||
yield self._check_for_updates(listener)
|
yield self._check_for_updates(listener)
|
||||||
|
|
||||||
if not timeout:
|
if not timeout:
|
||||||
_timeout_listener()
|
_timeout_listener()
|
||||||
|
else:
|
||||||
|
self.clock.call_later(timeout/1000.0, _timeout_listener)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014, 2015 OpenMarket Ltd
|
# Copyright 2015 OpenMarket Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -12,38 +12,3 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# 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 . import (
|
|
||||||
room, events, register, login, profile, presence, initial_sync, directory,
|
|
||||||
voip, admin, pusher, push_rule
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RestServletFactory(object):
|
|
||||||
|
|
||||||
""" A factory for creating REST servlets.
|
|
||||||
|
|
||||||
These REST servlets represent the entire client-server REST API. Generally
|
|
||||||
speaking, they serve as wrappers around events and the handlers that
|
|
||||||
process them.
|
|
||||||
|
|
||||||
See synapse.events for information on synapse events.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, hs):
|
|
||||||
client_resource = hs.get_resource_for_client()
|
|
||||||
|
|
||||||
# TODO(erikj): There *must* be a better way of doing this.
|
|
||||||
room.register_servlets(hs, client_resource)
|
|
||||||
events.register_servlets(hs, client_resource)
|
|
||||||
register.register_servlets(hs, client_resource)
|
|
||||||
login.register_servlets(hs, client_resource)
|
|
||||||
profile.register_servlets(hs, client_resource)
|
|
||||||
presence.register_servlets(hs, client_resource)
|
|
||||||
initial_sync.register_servlets(hs, client_resource)
|
|
||||||
directory.register_servlets(hs, client_resource)
|
|
||||||
voip.register_servlets(hs, client_resource)
|
|
||||||
admin.register_servlets(hs, client_resource)
|
|
||||||
pusher.register_servlets(hs, client_resource)
|
|
||||||
push_rule.register_servlets(hs, client_resource)
|
|
||||||
|
|
14
synapse/rest/client/__init__.py
Normal file
14
synapse/rest/client/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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.
|
49
synapse/rest/client/v1/__init__.py
Normal file
49
synapse/rest/client/v1/__init__.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2014, 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.
|
||||||
|
|
||||||
|
|
||||||
|
from . import (
|
||||||
|
room, events, register, login, profile, presence, initial_sync, directory,
|
||||||
|
voip, admin, pusher, push_rule
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RestServletFactory(object):
|
||||||
|
|
||||||
|
""" A factory for creating REST servlets.
|
||||||
|
|
||||||
|
These REST servlets represent the entire client-server REST API. Generally
|
||||||
|
speaking, they serve as wrappers around events and the handlers that
|
||||||
|
process them.
|
||||||
|
|
||||||
|
See synapse.events for information on synapse events.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
client_resource = hs.get_resource_for_client()
|
||||||
|
|
||||||
|
# TODO(erikj): There *must* be a better way of doing this.
|
||||||
|
room.register_servlets(hs, client_resource)
|
||||||
|
events.register_servlets(hs, client_resource)
|
||||||
|
register.register_servlets(hs, client_resource)
|
||||||
|
login.register_servlets(hs, client_resource)
|
||||||
|
profile.register_servlets(hs, client_resource)
|
||||||
|
presence.register_servlets(hs, client_resource)
|
||||||
|
initial_sync.register_servlets(hs, client_resource)
|
||||||
|
directory.register_servlets(hs, client_resource)
|
||||||
|
voip.register_servlets(hs, client_resource)
|
||||||
|
admin.register_servlets(hs, client_resource)
|
||||||
|
pusher.register_servlets(hs, client_resource)
|
||||||
|
push_rule.register_servlets(hs, client_resource)
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
""" This module contains base REST classes for constructing REST servlets. """
|
""" This module contains base REST classes for constructing REST servlets. """
|
||||||
from synapse.api.urls import CLIENT_PREFIX
|
from synapse.api.urls import CLIENT_PREFIX
|
||||||
from synapse.rest.transactions import HttpTransactionStore
|
from .transactions import HttpTransactionStore
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import logging
|
import logging
|
|
@ -18,7 +18,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.streams.config import PaginationConfig
|
from synapse.streams.config import PaginationConfig
|
||||||
from synapse.rest.base import RestServlet, client_path_pattern
|
from .base import RestServlet, client_path_pattern
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
@ -246,7 +246,7 @@ class JoinRoomAliasServlet(RestServlet):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {"room_id": identifier.to_string()}))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_PUT(self, request, room_identifier, txn_id):
|
def on_PUT(self, request, room_identifier, txn_id):
|
|
@ -24,7 +24,7 @@ from synapse.events.utils import serialize_event
|
||||||
from synapse.notifier import Notifier
|
from synapse.notifier import Notifier
|
||||||
from synapse.api.auth import Auth
|
from synapse.api.auth import Auth
|
||||||
from synapse.handlers import Handlers
|
from synapse.handlers import Handlers
|
||||||
from synapse.rest import RestServletFactory
|
from synapse.rest.client.v1 import RestServletFactory
|
||||||
from synapse.state import StateHandler
|
from synapse.state import StateHandler
|
||||||
from synapse.storage import DataStore
|
from synapse.storage import DataStore
|
||||||
from synapse.types import UserID, RoomAlias, RoomID, EventID
|
from synapse.types import UserID, RoomAlias, RoomID, EventID
|
||||||
|
|
|
@ -58,13 +58,6 @@ class RoomStore(SQLBaseStore):
|
||||||
logger.error("store_room with room_id=%s failed: %s", room_id, e)
|
logger.error("store_room with room_id=%s failed: %s", room_id, e)
|
||||||
raise StoreError(500, "Problem creating room.")
|
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):
|
def get_room(self, room_id):
|
||||||
"""Retrieve a room.
|
"""Retrieve a room.
|
||||||
|
|
||||||
|
|
|
@ -78,12 +78,6 @@ class StateStore(SQLBaseStore):
|
||||||
f,
|
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):
|
def _store_state_groups_txn(self, txn, event, context):
|
||||||
if context.current_state is None:
|
if context.current_state is None:
|
||||||
return
|
return
|
||||||
|
|
|
@ -39,6 +39,8 @@ from ._base import SQLBaseStore
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.util.logutils import log_function
|
from synapse.util.logutils import log_function
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,91 +54,79 @@ _STREAM_TOKEN = "stream"
|
||||||
_TOPOLOGICAL_TOKEN = "topological"
|
_TOPOLOGICAL_TOKEN = "topological"
|
||||||
|
|
||||||
|
|
||||||
def _parse_stream_token(string):
|
class _StreamToken(namedtuple("_StreamToken", "topological stream")):
|
||||||
try:
|
"""Tokens are positions between events. The token "s1" comes after event 1.
|
||||||
if string[0] != 's':
|
|
||||||
raise
|
|
||||||
return int(string[1:])
|
|
||||||
except:
|
|
||||||
raise SynapseError(400, "Invalid token")
|
|
||||||
|
|
||||||
|
s0 s1
|
||||||
|
| |
|
||||||
|
[0] V [1] V [2]
|
||||||
|
|
||||||
def _parse_topological_token(string):
|
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.
|
||||||
|
|
||||||
|
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__ = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, string):
|
||||||
try:
|
try:
|
||||||
if string[0] != 't':
|
if string[0] == 's':
|
||||||
raise
|
return cls(None, int(string[1:]))
|
||||||
|
if string[0] == 't':
|
||||||
parts = string[1:].split('-', 1)
|
parts = string[1:].split('-', 1)
|
||||||
return (int(parts[0]), int(parts[1]))
|
return cls(int(parts[1]), int(parts[0]))
|
||||||
except:
|
|
||||||
raise SynapseError(400, "Invalid token")
|
|
||||||
|
|
||||||
|
|
||||||
def is_stream_token(string):
|
|
||||||
try:
|
|
||||||
_parse_stream_token(string)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_topological_token(string):
|
|
||||||
try:
|
|
||||||
_parse_topological_token(string)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _get_token_bound(token, comparison):
|
|
||||||
try:
|
|
||||||
s = _parse_stream_token(token)
|
|
||||||
return "%s %s %d" % ("stream_ordering", comparison, s)
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
raise SynapseError(400, "Invalid token %r" % (string,))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_stream_token(cls, string):
|
||||||
try:
|
try:
|
||||||
top, stream = _parse_topological_token(token)
|
if string[0] == 's':
|
||||||
return "%s %s %d AND %s %s %d" % (
|
return cls(None, int(string[1:]))
|
||||||
"topological_ordering", comparison, top,
|
except:
|
||||||
"stream_ordering", comparison, stream,
|
pass
|
||||||
|
raise SynapseError(400, "Invalid token %r" % (string,))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.topological is not None:
|
||||||
|
return "t%d-%d" % (self.topological, self.stream)
|
||||||
|
else:
|
||||||
|
return "s%d" % (self.stream,)
|
||||||
|
|
||||||
|
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",
|
||||||
)
|
)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
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):
|
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
|
@log_function
|
||||||
def get_room_events_stream(self, user_id, from_key, to_key, room_id,
|
def get_room_events_stream(self, user_id, from_key, to_key, room_id,
|
||||||
limit=0, with_feedback=False):
|
limit=0, with_feedback=False):
|
||||||
|
@ -162,8 +152,8 @@ class StreamStore(SQLBaseStore):
|
||||||
limit = MAX_STREAM_SIZE
|
limit = MAX_STREAM_SIZE
|
||||||
|
|
||||||
# From and to keys should be integers from ordering.
|
# From and to keys should be integers from ordering.
|
||||||
from_id = _parse_stream_token(from_key)
|
from_id = _StreamToken.parse_stream_token(from_key)
|
||||||
to_id = _parse_stream_token(to_key)
|
to_id = _StreamToken.parse_stream_token(to_key)
|
||||||
|
|
||||||
if from_key == to_key:
|
if from_key == to_key:
|
||||||
return defer.succeed(([], to_key))
|
return defer.succeed(([], to_key))
|
||||||
|
@ -181,7 +171,7 @@ class StreamStore(SQLBaseStore):
|
||||||
}
|
}
|
||||||
|
|
||||||
def f(txn):
|
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)
|
rows = self.cursor_to_dict(txn)
|
||||||
|
|
||||||
|
@ -211,16 +201,20 @@ class StreamStore(SQLBaseStore):
|
||||||
# Tokens really represent positions between elements, but we use
|
# Tokens really represent positions between elements, but we use
|
||||||
# the convention of pointing to the event before the gap. Hence
|
# the convention of pointing to the event before the gap. Hence
|
||||||
# we have a bit of asymmetry when it comes to equalities.
|
# 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]
|
args = [room_id]
|
||||||
|
if direction == 'b':
|
||||||
bounds = _get_token_bound(from_key, from_comp)
|
order = "DESC"
|
||||||
|
bounds = _StreamToken.parse(from_key).upper_bound()
|
||||||
if to_key:
|
if to_key:
|
||||||
bounds = "%s AND %s" % (
|
bounds = "%s AND %s" % (
|
||||||
bounds, _get_token_bound(to_key, to_comp)
|
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:
|
if int(limit) > 0:
|
||||||
|
@ -249,9 +243,13 @@ class StreamStore(SQLBaseStore):
|
||||||
topo = rows[-1]["topological_ordering"]
|
topo = rows[-1]["topological_ordering"]
|
||||||
toke = rows[-1]["stream_ordering"]
|
toke = rows[-1]["stream_ordering"]
|
||||||
if direction == 'b':
|
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
|
toke -= 1
|
||||||
next_token = "t%s-%s" % (topo, toke)
|
next_token = str(_StreamToken(topo, toke))
|
||||||
else:
|
else:
|
||||||
# TODO (erikj): We should work out what to do here instead.
|
# TODO (erikj): We should work out what to do here instead.
|
||||||
next_token = to_key if to_key else from_key
|
next_token = to_key if to_key else from_key
|
||||||
|
@ -284,13 +282,14 @@ class StreamStore(SQLBaseStore):
|
||||||
rows.reverse() # As we selected with reverse ordering
|
rows.reverse() # As we selected with reverse ordering
|
||||||
|
|
||||||
if rows:
|
if rows:
|
||||||
# XXX: Always subtract 1 since the start token always goes
|
# Tokens are positions between events.
|
||||||
# backwards (parity with paginate_room_events). It isn't
|
# This token points *after* the last event in the chunk.
|
||||||
# obvious that this is correct; we should clarify the algorithm
|
# We need it to point to the event before it in the chunk
|
||||||
# used here.
|
# since we are going backwards so we subtract one from the
|
||||||
topo = rows[0]["topological_ordering"] - 1
|
# stream part.
|
||||||
|
topo = rows[0]["topological_ordering"]
|
||||||
toke = rows[0]["stream_ordering"] - 1
|
toke = rows[0]["stream_ordering"] - 1
|
||||||
start_token = "t%s-%s" % (topo, toke)
|
start_token = str(_StreamToken(topo, toke))
|
||||||
|
|
||||||
token = (start_token, end_token)
|
token = (start_token, end_token)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -59,23 +59,29 @@ class JustPresenceHandlers(object):
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.presence_handler = PresenceHandler(hs)
|
self.presence_handler = PresenceHandler(hs)
|
||||||
|
|
||||||
class PresenceStateTestCase(unittest.TestCase):
|
|
||||||
""" Tests presence management. """
|
|
||||||
|
|
||||||
|
class PresenceTestCase(unittest.TestCase):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
db_pool = SQLiteMemoryDbPool()
|
db_pool = SQLiteMemoryDbPool()
|
||||||
yield db_pool.prepare()
|
yield db_pool.prepare()
|
||||||
|
|
||||||
|
self.clock = MockClock()
|
||||||
|
|
||||||
self.mock_config = NonCallableMock()
|
self.mock_config = NonCallableMock()
|
||||||
self.mock_config.signing_key = [MockKey()]
|
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",
|
hs = HomeServer("test",
|
||||||
clock=MockClock(),
|
clock=self.clock,
|
||||||
db_pool=db_pool,
|
db_pool=db_pool,
|
||||||
handlers=None,
|
handlers=None,
|
||||||
resource_for_federation=Mock(),
|
resource_for_federation=self.mock_federation_resource,
|
||||||
http_client=None,
|
http_client=self.mock_http_client,
|
||||||
config=self.mock_config,
|
config=self.mock_config,
|
||||||
keyring=Mock(),
|
keyring=Mock(),
|
||||||
)
|
)
|
||||||
|
@ -92,24 +98,33 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
self.u_banana = hs.parse_userid("@banana:test")
|
self.u_banana = hs.parse_userid("@banana:test")
|
||||||
self.u_clementine = hs.parse_userid("@clementine: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(
|
yield self.store.set_presence_state(
|
||||||
self.u_apple.localpart, {"state": ONLINE, "status_msg": "Online"}
|
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.handler = hs.get_handlers().presence_handler
|
||||||
|
|
||||||
|
self.room_id = "a-room"
|
||||||
self.room_members = []
|
self.room_members = []
|
||||||
|
|
||||||
def get_rooms_for_user(user):
|
def get_rooms_for_user(user):
|
||||||
if user in self.room_members:
|
if user in self.room_members:
|
||||||
return defer.succeed(["a-room"])
|
return defer.succeed([self.room_id])
|
||||||
else:
|
else:
|
||||||
return defer.succeed([])
|
return defer.succeed([])
|
||||||
room_member_handler.get_rooms_for_user = get_rooms_for_user
|
room_member_handler.get_rooms_for_user = get_rooms_for_user
|
||||||
|
|
||||||
def get_room_members(room_id):
|
def get_room_members(room_id):
|
||||||
if room_id == "a-room":
|
if room_id == self.room_id:
|
||||||
return defer.succeed(self.room_members)
|
return defer.succeed(self.room_members)
|
||||||
else:
|
else:
|
||||||
return defer.succeed([])
|
return defer.succeed([])
|
||||||
|
@ -128,6 +143,10 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
self.handler.start_polling_presence = self.mock_start
|
self.handler.start_polling_presence = self.mock_start
|
||||||
self.handler.stop_polling_presence = self.mock_stop
|
self.handler.stop_polling_presence = self.mock_stop
|
||||||
|
|
||||||
|
|
||||||
|
class PresenceStateTestCase(PresenceTestCase):
|
||||||
|
""" Tests presence management. """
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_get_my_state(self):
|
def test_get_my_state(self):
|
||||||
state = yield self.handler.get_state(
|
state = yield self.handler.get_state(
|
||||||
|
@ -206,56 +225,9 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
self.mock_stop.assert_called_with(self.u_apple)
|
self.mock_stop.assert_called_with(self.u_apple)
|
||||||
|
|
||||||
|
|
||||||
class PresenceInvitesTestCase(unittest.TestCase):
|
class PresenceInvitesTestCase(PresenceTestCase):
|
||||||
""" Tests presence management. """
|
""" 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
|
@defer.inlineCallbacks
|
||||||
def test_invite_local(self):
|
def test_invite_local(self):
|
||||||
# TODO(paul): This test will likely break if/when real auth permissions
|
# 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_member_handler = hs.handlers.room_member_handler
|
||||||
|
|
||||||
|
self.room_id = "a-room"
|
||||||
self.room_members = []
|
self.room_members = []
|
||||||
|
|
||||||
def get_rooms_for_user(user):
|
def get_rooms_for_user(user):
|
||||||
if user in self.room_members:
|
if user in self.room_members:
|
||||||
return defer.succeed(["a-room"])
|
return defer.succeed([self.room_id])
|
||||||
else:
|
else:
|
||||||
return defer.succeed([])
|
return defer.succeed([])
|
||||||
self.room_member_handler.get_rooms_for_user = get_rooms_for_user
|
self.room_member_handler.get_rooms_for_user = get_rooms_for_user
|
||||||
|
|
||||||
def get_room_members(room_id):
|
def get_room_members(room_id):
|
||||||
if room_id == "a-room":
|
if room_id == self.room_id:
|
||||||
return defer.succeed(self.room_members)
|
return defer.succeed(self.room_members)
|
||||||
else:
|
else:
|
||||||
return defer.succeed([])
|
return defer.succeed([])
|
||||||
self.room_member_handler.get_room_members = get_room_members
|
self.room_member_handler.get_room_members = get_room_members
|
||||||
|
|
||||||
def get_room_hosts(room_id):
|
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])
|
hosts = set([u.domain for u in self.room_members])
|
||||||
return defer.succeed(hosts)
|
return defer.succeed(hosts)
|
||||||
else:
|
else:
|
||||||
|
@ -911,7 +884,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
||||||
"a-room"
|
self.room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
self.room_members.append(self.u_clementine)
|
self.room_members.append(self.u_clementine)
|
||||||
|
@ -974,7 +947,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
self.room_members = [self.u_apple, self.u_banana]
|
self.room_members = [self.u_apple, self.u_banana]
|
||||||
|
|
||||||
yield self.distributor.fire("user_joined_room", self.u_potato,
|
yield self.distributor.fire("user_joined_room", self.u_potato,
|
||||||
"a-room"
|
self.room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
yield put_json.await_calls()
|
yield put_json.await_calls()
|
||||||
|
@ -1003,7 +976,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
self.room_members.append(self.u_potato)
|
self.room_members.append(self.u_potato)
|
||||||
|
|
||||||
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
||||||
"a-room"
|
self.room_id
|
||||||
)
|
)
|
||||||
|
|
||||||
put_json.await_calls()
|
put_json.await_calls()
|
||||||
|
|
|
@ -223,7 +223,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||||
yield room_handler.change_membership(event, context)
|
yield room_handler.change_membership(event, context)
|
||||||
|
|
||||||
self.federation.handle_new_event.assert_called_once_with(
|
self.federation.handle_new_event.assert_called_once_with(
|
||||||
event, None, destinations=set()
|
event, destinations=set()
|
||||||
)
|
)
|
||||||
|
|
||||||
self.datastore.persist_event.assert_called_once_with(
|
self.datastore.persist_event.assert_called_once_with(
|
||||||
|
@ -301,7 +301,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||||
yield room_handler.change_membership(event, context)
|
yield room_handler.change_membership(event, context)
|
||||||
|
|
||||||
self.federation.handle_new_event.assert_called_once_with(
|
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(
|
self.datastore.persist_event.assert_called_once_with(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014 OpenMarket Ltd
|
# Copyright 2015 OpenMarket Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -12,4 +12,3 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# 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.
|
||||||
|
|
||||||
|
|
14
tests/rest/client/__init__.py
Normal file
14
tests/rest/client/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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.
|
15
tests/rest/client/v1/__init__.py
Normal file
15
tests/rest/client/v1/__init__.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2014 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.
|
||||||
|
|
|
@ -19,13 +19,13 @@ from tests import unittest
|
||||||
# twisted imports
|
# twisted imports
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import synapse.rest.events
|
import synapse.rest.client.v1.events
|
||||||
import synapse.rest.register
|
import synapse.rest.client.v1.register
|
||||||
import synapse.rest.room
|
import synapse.rest.client.v1.room
|
||||||
|
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
from ..utils import MockHttpResource, SQLiteMemoryDbPool, MockKey
|
from ....utils import MockHttpResource, SQLiteMemoryDbPool, MockKey
|
||||||
from .utils import RestTestCase
|
from .utils import RestTestCase
|
||||||
|
|
||||||
from mock import Mock, NonCallableMock
|
from mock import Mock, NonCallableMock
|
||||||
|
@ -144,9 +144,9 @@ class EventStreamPermissionsTestCase(RestTestCase):
|
||||||
hs.get_clock().time_msec.return_value = 1000000
|
hs.get_clock().time_msec.return_value = 1000000
|
||||||
hs.get_clock().time.return_value = 1000
|
hs.get_clock().time.return_value = 1000
|
||||||
|
|
||||||
synapse.rest.register.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.register.register_servlets(hs, self.mock_resource)
|
||||||
synapse.rest.events.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.events.register_servlets(hs, self.mock_resource)
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
# register an account
|
# register an account
|
||||||
self.user_id = "sid1"
|
self.user_id = "sid1"
|
|
@ -20,7 +20,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
||||||
from ..utils import MockHttpResource, MockKey
|
from ....utils import MockHttpResource, MockKey
|
||||||
|
|
||||||
from synapse.api.constants import PresenceState
|
from synapse.api.constants import PresenceState
|
||||||
from synapse.handlers.presence import PresenceHandler
|
from synapse.handlers.presence import PresenceHandler
|
|
@ -20,7 +20,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from mock import Mock, NonCallableMock
|
from mock import Mock, NonCallableMock
|
||||||
|
|
||||||
from ..utils import MockHttpResource, MockKey
|
from ....utils import MockHttpResource, MockKey
|
||||||
|
|
||||||
from synapse.api.errors import SynapseError, AuthError
|
from synapse.api.errors import SynapseError, AuthError
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
|
@ -18,7 +18,7 @@
|
||||||
# twisted imports
|
# twisted imports
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import synapse.rest.room
|
import synapse.rest.client.v1.room
|
||||||
from synapse.api.constants import Membership
|
from synapse.api.constants import Membership
|
||||||
|
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
@ -30,7 +30,7 @@ import json
|
||||||
import urllib
|
import urllib
|
||||||
import types
|
import types
|
||||||
|
|
||||||
from ..utils import MockHttpResource, SQLiteMemoryDbPool, MockKey
|
from ....utils import MockHttpResource, SQLiteMemoryDbPool, MockKey
|
||||||
from .utils import RestTestCase
|
from .utils import RestTestCase
|
||||||
|
|
||||||
from mock import Mock, NonCallableMock
|
from mock import Mock, NonCallableMock
|
||||||
|
@ -82,7 +82,7 @@ class RoomPermissionsTestCase(RestTestCase):
|
||||||
|
|
||||||
self.auth_user_id = self.rmcreator_id
|
self.auth_user_id = self.rmcreator_id
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
self.auth = hs.get_auth()
|
self.auth = hs.get_auth()
|
||||||
|
|
||||||
|
@ -476,7 +476,7 @@ class RoomsMemberListTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
@ -565,7 +565,7 @@ class RoomsCreateTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
@ -668,7 +668,7 @@ class RoomTopicTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
# create the room
|
# create the room
|
||||||
self.room_id = yield self.create_room_as(self.user_id)
|
self.room_id = yield self.create_room_as(self.user_id)
|
||||||
|
@ -783,7 +783,7 @@ class RoomMemberStateTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
self.room_id = yield self.create_room_as(self.user_id)
|
self.room_id = yield self.create_room_as(self.user_id)
|
||||||
|
|
||||||
|
@ -919,7 +919,7 @@ class RoomMessagesTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
self.room_id = yield self.create_room_as(self.user_id)
|
self.room_id = yield self.create_room_as(self.user_id)
|
||||||
|
|
||||||
|
@ -1023,7 +1023,7 @@ class RoomInitialSyncTestCase(RestTestCase):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
hs.get_datastore().insert_client_ip = _insert_client_ip
|
hs.get_datastore().insert_client_ip = _insert_client_ip
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
# Since I'm getting my own presence I need to exist as far as presence
|
# Since I'm getting my own presence I need to exist as far as presence
|
||||||
# is concerned.
|
# is concerned.
|
|
@ -18,10 +18,10 @@
|
||||||
# twisted imports
|
# twisted imports
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import synapse.rest.room
|
import synapse.rest.client.v1.room
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
from ..utils import MockHttpResource, MockClock, SQLiteMemoryDbPool, MockKey
|
from ....utils import MockHttpResource, MockClock, SQLiteMemoryDbPool, MockKey
|
||||||
from .utils import RestTestCase
|
from .utils import RestTestCase
|
||||||
|
|
||||||
from mock import Mock, NonCallableMock
|
from mock import Mock, NonCallableMock
|
||||||
|
@ -104,7 +104,7 @@ class RoomTypingTestCase(RestTestCase):
|
||||||
hs.get_handlers().room_member_handler.fetch_room_distributions_into = (
|
hs.get_handlers().room_member_handler.fetch_room_distributions_into = (
|
||||||
fetch_room_distributions_into)
|
fetch_room_distributions_into)
|
||||||
|
|
||||||
synapse.rest.room.register_servlets(hs, self.mock_resource)
|
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
|
||||||
|
|
||||||
self.room_id = yield self.create_room_as(self.user_id)
|
self.room_id = yield self.create_room_as(self.user_id)
|
||||||
# Need another user to make notifications actually work
|
# Need another user to make notifications actually work
|
|
@ -1,5 +0,0 @@
|
||||||
synapse/storage/feedback.py
|
|
||||||
synapse/storage/keys.py
|
|
||||||
synapse/storage/pdu.py
|
|
||||||
synapse/storage/stream.py
|
|
||||||
synapse/storage/transactions.py
|
|
|
@ -56,17 +56,6 @@ class RoomStoreTestCase(unittest.TestCase):
|
||||||
(yield self.store.get_room(self.room.to_string()))
|
(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
|
@defer.inlineCallbacks
|
||||||
def test_get_rooms(self):
|
def test_get_rooms(self):
|
||||||
# get_rooms does an INNER JOIN on the room_aliases table :(
|
# get_rooms does an INNER JOIN on the room_aliases table :(
|
||||||
|
|
|
@ -138,7 +138,8 @@ class MockClock(object):
|
||||||
now = 1000
|
now = 1000
|
||||||
|
|
||||||
def __init__(self):
|
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 = []
|
self.timers = []
|
||||||
|
|
||||||
def time(self):
|
def time(self):
|
||||||
|
@ -154,11 +155,16 @@ class MockClock(object):
|
||||||
LoggingContext.thread_local.current_context = current_context
|
LoggingContext.thread_local.current_context = current_context
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
t = (self.now + delay, wrapped_callback)
|
t = [self.now + delay, wrapped_callback, False]
|
||||||
self.timers.append(t)
|
self.timers.append(t)
|
||||||
|
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def cancel_call_later(self, timer):
|
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]
|
self.timers = [t for t in self.timers if t != timer]
|
||||||
|
|
||||||
# For unit testing
|
# For unit testing
|
||||||
|
@ -168,11 +174,17 @@ class MockClock(object):
|
||||||
timers = self.timers
|
timers = self.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:
|
if self.now >= time:
|
||||||
|
t[2] = True
|
||||||
callback()
|
callback()
|
||||||
else:
|
else:
|
||||||
self.timers.append((time, callback))
|
self.timers.append(t)
|
||||||
|
|
||||||
|
|
||||||
class SQLiteMemoryDbPool(ConnectionPool, object):
|
class SQLiteMemoryDbPool(ConnectionPool, object):
|
||||||
|
|
Loading…
Reference in a new issue