From b4e1c1f51e42dee478443323a331499ce91cf8a7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 5 Sep 2014 12:46:48 -0700 Subject: [PATCH 01/23] Minor spec tweaks. --- docs/specification.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index 239e51b4f..a329c0754 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -742,15 +742,17 @@ There are several APIs provided to ``GET`` events for a room: Description: Get all ``m.room.member`` state events. Response format: - ``{ "start": "token", "end": "token", "chunk": [ { m.room.member event }, ... ] }`` + ``{ "start": "", "end": "", "chunk": [ { m.room.member event }, ... ] }`` Example: TODO |/rooms//messages|_ Description: - Get all ``m.room.message`` events. + Get all ``m.room.message`` and ``m.room.member`` events. This API supports pagination + using ``from`` and ``to`` query parameters, coupled with the ``start`` and ``end`` + tokens from an |initialSync|_ API. Response format: - ``{ TODO }`` + ``{ "start": "", "end": "" }`` Example: TODO From 64b6f09b0d2136394deb46c83e9ba55f044fcbec Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 6 Sep 2014 17:48:16 -0700 Subject: [PATCH 02/23] fix embarassing bug where in-progress messages get vaped when the previous one gets delivered --- webclient/room/room-controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index c702917fe..c8ca771b2 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -485,7 +485,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) promise.then( function() { console.log("Request successfully sent"); - $scope.textInput = ""; + if (!echo) { + $scope.textInput = ""; + } /* if (echoMessage) { // Remove the fake echo message from the room messages From 667e747ed11a418da317a03fc3c59a205c5c4af0 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 17:56:21 +0100 Subject: [PATCH 03/23] Fix bug where we no longer stored user_id on Pdus --- synapse/storage/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py index ad2a484c1..9201a377b 100644 --- a/synapse/storage/__init__.py +++ b/synapse/storage/__init__.py @@ -36,7 +36,7 @@ from .registration import RegistrationStore from .room import RoomStore from .roommember import RoomMemberStore from .stream import StreamStore -from .pdu import StatePduStore, PduStore +from .pdu import StatePduStore, PduStore, PdusTable from .transactions import TransactionStore from .keys import KeyStore @@ -123,6 +123,12 @@ class DataStore(RoomMemberStore, RoomStore, del cols["content"] del cols["prev_pdus"] cols["content_json"] = json.dumps(pdu.content) + + unrec_keys.update({ + k: v for k, v in cols.items() + if k not in PdusTable.fields + }) + cols["unrecognized_keys"] = json.dumps(unrec_keys) logger.debug("Persisting: %s", repr(cols)) From 14975ce5bcf4dac2720cc4be290100a580334393 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 17:57:02 +0100 Subject: [PATCH 04/23] Fix bug where we relied on the current_state_events being updated when we are handling type specific persistence --- synapse/storage/roommember.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py index 20f22057a..676b2f265 100644 --- a/synapse/storage/roommember.py +++ b/synapse/storage/roommember.py @@ -66,8 +66,8 @@ class RoomMemberStore(SQLBaseStore): # Check if this was the last person to have left. member_events = self._get_members_query_txn( txn, - where_clause="c.room_id = ? AND m.membership = ?", - where_values=(event.room_id, Membership.JOIN,) + where_clause="c.room_id = ? AND m.membership = ? AND m.user_id != ?", + where_values=(event.room_id, Membership.JOIN, target_user_id,) ) joined_domains = set() From afb7f173cf3f04ffe212025910a950814d1761bc Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 18:13:05 +0100 Subject: [PATCH 05/23] Bump version and change log --- CHANGES.rst | 19 +++++++++++++++++++ VERSION | 2 +- synapse/__init__.py | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b9b3e9d0e..ec53438f5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,22 @@ +Changes in synapse 0.2.3 (2014-09-12) +===================================== + +Homeserver: + * Fix bug where we stopped sending events to remote home servers if a + user from that home server left, even if there were some still in the + room. + * Fix bugs in the state conflict resolution where it was incorrectly + rejecting events. + +Webclient: + * Display room names and topics. + * Allow setting/editing of room names and topics. + * Display information about rooms on the main page. + * Handle ban and kick events in real time. + * VoIP UI and reliabilty improvements. + * Improvements to initial startup speed. + * Don't display duplicate join events. + Changes in synapse 0.2.2 (2014-09-06) ===================================== diff --git a/VERSION b/VERSION index ee1372d33..717903969 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.2 +0.2.3 diff --git a/synapse/__init__.py b/synapse/__init__.py index 1ed9cdcdf..d60267ebe 100644 --- a/synapse/__init__.py +++ b/synapse/__init__.py @@ -16,4 +16,4 @@ """ This is a reference implementation of a synapse home server. """ -__version__ = "0.2.2" +__version__ = "0.2.3" From 842898df159f9e3c59570d9ba7dc127f26b682b8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Sep 2014 18:16:24 +0100 Subject: [PATCH 06/23] Send multiple candidates at once instead of all individually. Changes spec to include multiple candidates in a candidate(s) message. --- webclient/components/matrix/matrix-call.js | 59 +++++++++++++++++-- .../components/matrix/matrix-phone-service.js | 8 ++- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 2e3e2b096..fd21198d2 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -47,6 +47,10 @@ angular.module('MatrixCall', []) this.call_id = "c" + new Date().getTime(); this.state = 'fledgling'; this.didConnect = false; + + // a queue for candidates waiting to go out. We try to amalgamate candidates into a single candidate message where possible + this.candidateSendQueue = []; + this.candidateSendTries = 0; } MatrixCall.prototype.createPeerConnection = function() { @@ -174,12 +178,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.gotLocalIceCandidate = function(event) { console.log(event); if (event.candidate) { - var content = { - version: 0, - call_id: this.call_id, - candidate: event.candidate - }; - this.sendEventWithRetry('m.call.candidate', content); + this.sendCandidate(event.candidate); } } @@ -370,5 +369,53 @@ angular.module('MatrixCall', []) }, delayMs); }; + // Sends candidates with are sent in a special way because we try to amalgamate them into one message + MatrixCall.prototype.sendCandidate = function(content) { + this.candidateSendQueue.push(content); + var self = this; + if (this.candidateSendTries == 0) $timeout(function() { self.sendCandidateQueue(); }, 100); + }; + + MatrixCall.prototype.sendCandidateQueue = function(content) { + if (this.candidateSendQueue.length == 0) return; + + var cands = this.candidateSendQueue; + this.candidateSendQueue = []; + ++this.candidateSendTries; + var content = { + version: 0, + call_id: this.call_id, + candidates: cands + }; + var self = this; + console.log("Attempting to send "+cands.length+" candidates"); + matrixService.sendEvent(self.room_id, 'm.call.candidates', undefined, content).then(function() { self.candsSent(); }, function(error) { self.candsSendFailed(cands, error); } ); + }; + + MatrixCall.prototype.candsSent = function() { + this.candidateSendTries = 0; + this.sendCandidateQueue(); + }; + + MatrixCall.prototype.candsSendFailed = function(cands, error) { + for (var i = 0; i < cands.length; ++i) { + this.candidateSendQueue.push(cands[i]); + } + + if (this.candidateSendTries > 5) { + console.log("Failed to send candidates on attempt "+ev.tries+". Giving up for now."); + this.candidateSendTries = 0; + return; + } + + var delayMs = 500 * Math.pow(2, this.candidateSendTries); + ++this.candidateSendTries; + console.log("Failed to send candidates. Retrying in "+delayMs+"ms"); + var self = this; + $timeout(function() { + self.sendCandidateQueue(); + }, delayMs); + }; + return MatrixCall; }]); diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index b0dcf1910..2d0732a8d 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -77,13 +77,15 @@ angular.module('matrixPhoneService', []) return; } call.receivedAnswer(msg); - } else if (event.type == 'm.call.candidate') { + } else if (event.type == 'm.call.candidates') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { - console.log("Got candidate for unknown call ID "+msg.call_id); + console.log("Got candidates for unknown call ID "+msg.call_id); return; } - call.gotRemoteIceCandidate(msg.candidate); + for (var i = 0; i < msg.candidates.length; ++i) { + call.gotRemoteIceCandidate(msg.candidates[i]); + } } else if (event.type == 'm.call.hangup') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { From 21b45d2a5b8bd00cec9fa634cb8cc87b70e7c05c Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Sep 2014 18:18:35 +0100 Subject: [PATCH 07/23] Update the spec document to replace the candidate message with the candidates message. --- docs/specification.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index aad200c04..ab16a0bb6 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1182,16 +1182,16 @@ This event is sent by the caller when they wish to establish a call. - ``type`` : "string" - The type of session description, in this case 'offer' - ``sdp`` : "string" - The SDP text of the session description -``m.call.candidate`` +``m.call.candidates`` This event is sent by callers after sending an invite and by the callee after answering. -Its purpose is to give the other party an additional ICE candidate to try using to +Its purpose is to give the other party additional ICE candidates to try using to communicate. Required keys: - ``call_id`` : "string" - The ID of the call this event relates to - ``version`` : "integer" - The version of the VoIP specification this messages adheres to. his specification is version 0. - - ``candidate`` : "candidate object" - Object describing the candidate. + - ``candidates`` : "array of candidate objects" - Array of object describing the candidates. ``Candidate Object`` From 84326e24913132b96f2bb289fe560c0e053b65cf Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 18:26:19 +0100 Subject: [PATCH 08/23] Add note about glare support --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index ec53438f5..3076b29c7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,7 @@ Webclient: * Display information about rooms on the main page. * Handle ban and kick events in real time. * VoIP UI and reliabilty improvements. + * Add glare support for VoIP. * Improvements to initial startup speed. * Don't display duplicate join events. From 80852d11351d27de35f85041df5e5da93528738d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 18:27:04 +0100 Subject: [PATCH 09/23] Spellcheck --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3076b29c7..e602ad6f8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,7 +13,7 @@ Webclient: * Allow setting/editing of room names and topics. * Display information about rooms on the main page. * Handle ban and kick events in real time. - * VoIP UI and reliabilty improvements. + * VoIP UI and reliability improvements. * Add glare support for VoIP. * Improvements to initial startup speed. * Don't display duplicate join events. From 958b52596cea624ff25d98cc33e4cfa8721c0929 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 12 Sep 2014 18:36:45 +0100 Subject: [PATCH 10/23] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e602ad6f8..d3beea3ed 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -17,6 +17,9 @@ Webclient: * Add glare support for VoIP. * Improvements to initial startup speed. * Don't display duplicate join events. + * Local echo of messages. + * Differentiate sending and sent of local echo. + * Various minor bug fixes. Changes in synapse 0.2.2 (2014-09-06) ===================================== From cd62ee3f29456d96d336f4c67cbd37a0a95f7b4a Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 18:24:53 +0100 Subject: [PATCH 11/23] Have all unit tests import from our own subclass of trial's unittest TestCase; set up logging in ONE PLACE ONLY --- tests/api/test_ratelimiting.py | 2 +- tests/events/test_events.py | 2 +- tests/federation/test_federation.py | 6 +----- tests/federation/test_pdu_codec.py | 2 +- tests/handlers/test_directory.py | 6 +----- tests/handlers/test_federation.py | 6 +----- tests/handlers/test_presence.py | 7 +------ tests/handlers/test_presencelike.py | 6 +----- tests/handlers/test_profile.py | 6 +----- tests/handlers/test_room.py | 6 +----- tests/handlers/test_typing.py | 6 +----- tests/rest/test_events.py | 4 +--- tests/rest/test_presence.py | 6 +----- tests/rest/test_profile.py | 3 ++- tests/rest/utils.py | 2 +- tests/storage/test_base.py | 2 +- tests/test_distributor.py | 2 +- tests/test_state.py | 3 +-- tests/test_types.py | 2 +- tests/unittest.py | 30 +++++++++++++++++++++++++++++ tests/util/test_lock.py | 4 ++-- 21 files changed, 52 insertions(+), 61 deletions(-) create mode 100644 tests/unittest.py diff --git a/tests/api/test_ratelimiting.py b/tests/api/test_ratelimiting.py index dc2f83c7e..dd0bc19ec 100644 --- a/tests/api/test_ratelimiting.py +++ b/tests/api/test_ratelimiting.py @@ -1,6 +1,6 @@ from synapse.api.ratelimiting import Ratelimiter -import unittest +from tests import unittest class TestRatelimiter(unittest.TestCase): diff --git a/tests/events/test_events.py b/tests/events/test_events.py index 93d5c15c6..a4b6cb3af 100644 --- a/tests/events/test_events.py +++ b/tests/events/test_events.py @@ -15,7 +15,7 @@ from synapse.api.events import SynapseEvent -import unittest +from tests import unittest class SynapseTemplateCheckTestCase(unittest.TestCase): diff --git a/tests/federation/test_federation.py b/tests/federation/test_federation.py index 0b105fe72..954ccac2a 100644 --- a/tests/federation/test_federation.py +++ b/tests/federation/test_federation.py @@ -14,11 +14,10 @@ # trial imports from twisted.internet import defer -from twisted.trial import unittest +from tests import unittest # python imports from mock import Mock -import logging from ..utils import MockHttpResource, MockClock @@ -28,9 +27,6 @@ from synapse.federation.units import Pdu from synapse.storage.pdu import PduTuple, PduEntry -logging.getLogger().addHandler(logging.NullHandler()) - - def make_pdu(prev_pdus=[], **kwargs): """Provide some default fields for making a PduTuple.""" pdu_fields = { diff --git a/tests/federation/test_pdu_codec.py b/tests/federation/test_pdu_codec.py index 9f74ba119..344e1baf6 100644 --- a/tests/federation/test_pdu_codec.py +++ b/tests/federation/test_pdu_codec.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from twisted.trial import unittest +from tests import unittest from synapse.federation.pdu_codec import ( PduCodec, encode_event_id, decode_event_id diff --git a/tests/handlers/test_directory.py b/tests/handlers/test_directory.py index 72a2b1443..54d6e51f9 100644 --- a/tests/handlers/test_directory.py +++ b/tests/handlers/test_directory.py @@ -14,11 +14,10 @@ # limitations under the License. -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock -import logging from synapse.server import HomeServer from synapse.http.client import HttpClient @@ -26,9 +25,6 @@ from synapse.handlers.directory import DirectoryHandler from synapse.storage.directory import RoomAliasMapping -logging.getLogger().addHandler(logging.NullHandler()) - - class DirectoryHandlers(object): def __init__(self, hs): self.directory_handler = DirectoryHandler(hs) diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py index 6fc3d8f7f..f0308a29d 100644 --- a/tests/handlers/test_federation.py +++ b/tests/handlers/test_federation.py @@ -14,7 +14,7 @@ from twisted.internet import defer -from twisted.trial import unittest +from tests import unittest from synapse.api.events.room import ( InviteJoinEvent, MessageEvent, RoomMemberEvent @@ -26,12 +26,8 @@ from synapse.federation.units import Pdu from mock import NonCallableMock, ANY -import logging - from ..utils import get_mock_call_args -logging.getLogger().addHandler(logging.NullHandler()) - class FederationTestCase(unittest.TestCase): diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 9eb8b6909..06f5f9c2b 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -14,11 +14,10 @@ # limitations under the License. -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer, reactor from mock import Mock, call, ANY -import logging import json from ..utils import MockHttpResource, MockClock, DeferredMockCallable @@ -34,9 +33,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE ONLINE = PresenceState.ONLINE -logging.getLogger().addHandler(logging.NullHandler()) - - def _expect_edu(destination, edu_type, content, origin="test"): return { "origin": origin, @@ -92,7 +88,6 @@ class PresenceStateTestCase(unittest.TestCase): # Mock the RoomMemberHandler room_member_handler = Mock(spec=[]) hs.handlers.room_member_handler = room_member_handler - logging.getLogger().debug("Mocking room_member_handler=%r", room_member_handler) # Some local users to test with self.u_apple = hs.parse_userid("@apple:test") diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index b35980d94..72c55b366 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -16,11 +16,10 @@ """This file contains tests of the "presence-like" data that is shared between presence and profiles; namely, the displayname and avatar_url.""" -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock, call, ANY -import logging from ..utils import MockClock @@ -35,9 +34,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE ONLINE = PresenceState.ONLINE -logging.getLogger().addHandler(logging.NullHandler()) - - class MockReplication(object): def __init__(self): self.edu_handlers = {} diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py index 8e7a89b47..0a5cebb4c 100644 --- a/tests/handlers/test_profile.py +++ b/tests/handlers/test_profile.py @@ -14,20 +14,16 @@ # limitations under the License. -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock -import logging from synapse.api.errors import AuthError from synapse.server import HomeServer from synapse.handlers.profile import ProfileHandler -logging.getLogger().addHandler(logging.NullHandler()) - - class ProfileHandlers(object): def __init__(self, hs): self.profile_handler = ProfileHandler(hs) diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py index 5687bbea0..a1a2e8049 100644 --- a/tests/handlers/test_room.py +++ b/tests/handlers/test_room.py @@ -15,7 +15,7 @@ from twisted.internet import defer -from twisted.trial import unittest +from tests import unittest from synapse.api.events.room import ( InviteJoinEvent, RoomMemberEvent, RoomConfigEvent @@ -27,10 +27,6 @@ from synapse.server import HomeServer from mock import Mock, NonCallableMock -import logging - -logging.getLogger().addHandler(logging.NullHandler()) - class RoomMemberHandlerTestCase(unittest.TestCase): diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py index 6532ac94a..ab908cdfc 100644 --- a/tests/handlers/test_typing.py +++ b/tests/handlers/test_typing.py @@ -14,12 +14,11 @@ # limitations under the License. -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock, call, ANY import json -import logging from ..utils import MockHttpResource, MockClock, DeferredMockCallable @@ -27,9 +26,6 @@ from synapse.server import HomeServer from synapse.handlers.typing import TypingNotificationHandler -logging.getLogger().addHandler(logging.NullHandler()) - - def _expect_edu(destination, edu_type, content, origin="test"): return { "origin": origin, diff --git a/tests/rest/test_events.py b/tests/rest/test_events.py index fd2224f55..79b371c04 100644 --- a/tests/rest/test_events.py +++ b/tests/rest/test_events.py @@ -14,7 +14,7 @@ # limitations under the License. """ Tests REST events for /events paths.""" -from twisted.trial import unittest +from tests import unittest # twisted imports from twisted.internet import defer @@ -27,14 +27,12 @@ from synapse.server import HomeServer # python imports import json -import logging from ..utils import MockHttpResource, MemoryDataStore from .utils import RestTestCase from mock import Mock, NonCallableMock -logging.getLogger().addHandler(logging.NullHandler()) PATH_PREFIX = "/_matrix/client/api/v1" diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index a1db0fbcf..ea3478ac5 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -15,11 +15,10 @@ """Tests REST events for /presence paths.""" -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock -import logging from ..utils import MockHttpResource @@ -28,9 +27,6 @@ from synapse.handlers.presence import PresenceHandler from synapse.server import HomeServer -logging.getLogger().addHandler(logging.NullHandler()) - - OFFLINE = PresenceState.OFFLINE UNAVAILABLE = PresenceState.UNAVAILABLE ONLINE = PresenceState.ONLINE diff --git a/tests/rest/test_profile.py b/tests/rest/test_profile.py index f41810df1..e6e51f6dd 100644 --- a/tests/rest/test_profile.py +++ b/tests/rest/test_profile.py @@ -15,7 +15,7 @@ """Tests REST events for /profile paths.""" -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock @@ -28,6 +28,7 @@ from synapse.server import HomeServer myid = "@1234ABCD:test" PATH_PREFIX = "/_matrix/client/api/v1" + class ProfileTestCase(unittest.TestCase): """ Tests profile management. """ diff --git a/tests/rest/utils.py b/tests/rest/utils.py index 77f5ecf0d..ce2e8fd98 100644 --- a/tests/rest/utils.py +++ b/tests/rest/utils.py @@ -17,7 +17,7 @@ from twisted.internet import defer # trial imports -from twisted.trial import unittest +from tests import unittest from synapse.api.constants import Membership diff --git a/tests/storage/test_base.py b/tests/storage/test_base.py index 330311448..3ad9a4b0c 100644 --- a/tests/storage/test_base.py +++ b/tests/storage/test_base.py @@ -14,7 +14,7 @@ # limitations under the License. -from twisted.trial import unittest +from tests import unittest from twisted.internet import defer from mock import Mock, call diff --git a/tests/test_distributor.py b/tests/test_distributor.py index 04933f0ec..39c5b8dff 100644 --- a/tests/test_distributor.py +++ b/tests/test_distributor.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from tests import unittest from twisted.internet import defer -from twisted.trial import unittest from mock import Mock, patch diff --git a/tests/test_state.py b/tests/test_state.py index 16af95b7b..b1624f0b2 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from tests import unittest from twisted.internet import defer -from twisted.trial import unittest from twisted.python.log import PythonLoggingObserver from synapse.state import StateHandler @@ -26,7 +26,6 @@ from collections import namedtuple from mock import Mock -import logging import mock diff --git a/tests/test_types.py b/tests/test_types.py index 571938356..276ecc91f 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest +from tests import unittest from synapse.server import BaseHomeServer from synapse.types import UserID, RoomAlias diff --git a/tests/unittest.py b/tests/unittest.py new file mode 100644 index 000000000..00c3c532e --- /dev/null +++ b/tests/unittest.py @@ -0,0 +1,30 @@ +# -*- 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. + +from twisted.trial import unittest + +import logging + + +# logging doesn't have a "don't log anything at all EVARRRR setting, +# but since the highest value is 50, 1000000 should do ;) +NEVER = 1000000 + +logging.getLogger().addHandler(logging.StreamHandler()) +logging.getLogger().setLevel(NEVER) + + +class TestCase(unittest.TestCase): + pass diff --git a/tests/util/test_lock.py b/tests/util/test_lock.py index 5623d7842..6a1e521b1 100644 --- a/tests/util/test_lock.py +++ b/tests/util/test_lock.py @@ -15,7 +15,7 @@ from twisted.internet import defer -from twisted.trial import unittest +from tests import unittest from synapse.util.lockutils import LockManager @@ -105,4 +105,4 @@ class LockManagerTestCase(unittest.TestCase): pass with (yield self.lock_manager.lock(key)): - pass \ No newline at end of file + pass From ca8349a897c233d72ea74128dabdd1311f00c13c Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 18:29:07 +0100 Subject: [PATCH 12/23] Allow a TestCase to set a 'loglevel' attribute, which overrides the logging level while that testcase runs --- tests/unittest.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/unittest.py b/tests/unittest.py index 00c3c532e..19be03b96 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -27,4 +27,25 @@ logging.getLogger().setLevel(NEVER) class TestCase(unittest.TestCase): - pass + def __init__(self, *args, **kwargs): + super(TestCase, self).__init__(*args, **kwargs) + + level = getattr(self, "loglevel", NEVER) + + orig_setUp = self.setUp + + def setUp(): + old_level = logging.getLogger().level + + if old_level != level: + orig_tearDown = self.tearDown + + def tearDown(): + ret = orig_tearDown() + logging.getLogger().setLevel(old_level) + return ret + self.tearDown = tearDown + + logging.getLogger().setLevel(level) + return orig_setUp() + self.setUp = setUp From 33c4dd4c2ddcd81854855e84a838db9603bbe338 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 18:38:11 +0100 Subject: [PATCH 13/23] Define a (class) decorator for easily setting a DEBUG logging level on a TestCase --- tests/unittest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unittest.py b/tests/unittest.py index 19be03b96..c66a3b840 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -49,3 +49,8 @@ class TestCase(unittest.TestCase): logging.getLogger().setLevel(level) return orig_setUp() self.setUp = setUp + + +def DEBUG(target): + target.loglevel = logging.DEBUG + return target From d9f3f322c5f17a4c3d3ac000462a7bbd0a407711 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 18:43:49 +0100 Subject: [PATCH 14/23] Additionally look first for a 'loglevel' attribute on the running test method, before the TestCase --- tests/unittest.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unittest.py b/tests/unittest.py index c66a3b840..8ae724c78 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -27,10 +27,14 @@ logging.getLogger().setLevel(NEVER) class TestCase(unittest.TestCase): - def __init__(self, *args, **kwargs): - super(TestCase, self).__init__(*args, **kwargs) + def __init__(self, methodName, *args, **kwargs): + super(TestCase, self).__init__(methodName, *args, **kwargs) - level = getattr(self, "loglevel", NEVER) + method = getattr(self, methodName) + + level = getattr(method, "loglevel", + getattr(self, "loglevel", + NEVER)) orig_setUp = self.setUp From aeb69c0f8cc6e723316aefbc6b71c82b4ed94aad Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 18:45:48 +0100 Subject: [PATCH 15/23] Add some docstrings --- tests/unittest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unittest.py b/tests/unittest.py index 8ae724c78..e437d3541 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -27,6 +27,10 @@ logging.getLogger().setLevel(NEVER) class TestCase(unittest.TestCase): + """A subclass of twisted.trial's TestCase which looks for 'loglevel' + attributes on both itself and its individual test methods, to override the + root logger's logging level while that test (case|method) runs.""" + def __init__(self, methodName, *args, **kwargs): super(TestCase, self).__init__(methodName, *args, **kwargs) @@ -56,5 +60,7 @@ class TestCase(unittest.TestCase): def DEBUG(target): + """A decorator to set the .loglevel attribute to logging.DEBUG. + Can apply to either a TestCase or an individual test method.""" target.loglevel = logging.DEBUG return target From 7a77aabb4bbb997db9dadd46e49d855946c1ae2e Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 12 Sep 2014 19:07:29 +0100 Subject: [PATCH 16/23] Define a CLOS-like 'around' modifier as a decorator, to neaten up the 'orig_*' noise of wrapping the setUp()/tearDown() methods --- tests/unittest.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/unittest.py b/tests/unittest.py index e437d3541..fb97fb114 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -26,6 +26,23 @@ logging.getLogger().addHandler(logging.StreamHandler()) logging.getLogger().setLevel(NEVER) +def around(target): + """A CLOS-style 'around' modifier, which wraps the original method of the + given instance with another piece of code. + + @around(self) + def method_name(orig, *args, **kwargs): + return orig(*args, **kwargs) + """ + def _around(code): + name = code.__name__ + orig = getattr(target, name) + def new(*args, **kwargs): + return code(orig, *args, **kwargs) + setattr(target, name, new) + return _around + + class TestCase(unittest.TestCase): """A subclass of twisted.trial's TestCase which looks for 'loglevel' attributes on both itself and its individual test methods, to override the @@ -40,23 +57,19 @@ class TestCase(unittest.TestCase): getattr(self, "loglevel", NEVER)) - orig_setUp = self.setUp - - def setUp(): + @around(self) + def setUp(orig): old_level = logging.getLogger().level if old_level != level: - orig_tearDown = self.tearDown - - def tearDown(): - ret = orig_tearDown() + @around(self) + def tearDown(orig): + ret = orig() logging.getLogger().setLevel(old_level) return ret - self.tearDown = tearDown logging.getLogger().setLevel(level) - return orig_setUp() - self.setUp = setUp + return orig() def DEBUG(target): From 276b9f1839424e62da1c546acc07053a2ce98001 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 8 Sep 2014 10:01:15 -0700 Subject: [PATCH 17/23] more wishlist --- WISHLIST.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WISHLIST.rst b/WISHLIST.rst index 68324ad9d..a0713f196 100644 --- a/WISHLIST.rst +++ b/WISHLIST.rst @@ -5,3 +5,5 @@ Broad-sweeping stuff which would be nice to have - homeserver implementation in go - homeserver implementation in node.js - client SDKs + - libpurple library + - irssi plugin? From 32acb7e90304fccf85e2271fb0a6f60b1fadbaf7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 13 Sep 2014 11:35:36 +0100 Subject: [PATCH 18/23] always scroll to bottom when entering a room --- webclient/room/room-controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 45dfff95c..50d902ae4 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -220,7 +220,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) }; var paginate = function(numItems) { - // console.log("paginate " + numItems); + //console.log("paginate " + numItems + " and first_pagination is " + $scope.state.first_pagination); if ($scope.state.paginating || !$scope.room_id) { return; } @@ -260,7 +260,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) } if ($scope.state.first_pagination) { - scrollToBottom(); + scrollToBottom(true); $scope.state.first_pagination = false; } else { From 49b5dd56b53f0bb6a7ce678eb4b0ecd158df1eb3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 13 Sep 2014 11:38:45 +0100 Subject: [PATCH 19/23] unbreak wordwrapping by breaking multiline paste for now --- webclient/app.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webclient/app.css b/webclient/app.css index a277bd2a5..064f626f0 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -529,7 +529,8 @@ a:active { color: #000; } .bubble .message { /* Break lines when encountering CR+LF */ - white-space: pre; + /* FIXME: this breaks wordwrapping. We need to s#CRLF#
#g instead */ +/* white-space: pre; */ } .bubble .messagePending { opacity: 0.3 From f3d3441d02afdb83a082c292a5f53d0c08412835 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 10:22:57 +0200 Subject: [PATCH 20/23] Use "white-space: pre-wrap" for "Text will wrap when necessary, and on line breaks" --- webclient/app.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/webclient/app.css b/webclient/app.css index 064f626f0..4a4ba7b8f 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -528,9 +528,8 @@ a:active { color: #000; } } .bubble .message { - /* Break lines when encountering CR+LF */ - /* FIXME: this breaks wordwrapping. We need to s#CRLF#
#g instead */ -/* white-space: pre; */ + /* Wrap words and break lines on CR+LF */ + white-space: pre-wrap; } .bubble .messagePending { opacity: 0.3 From a9da2ec895ad5caccda16e60e40f21360c6a5a73 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 10:39:30 +0200 Subject: [PATCH 21/23] BF: presence and eventMap were not reset at logout. --- .../matrix/event-handler-service.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 705a5a07f..4604ff619 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -38,6 +38,13 @@ angular.module('eventHandlerService', []) var TOPIC_EVENT = "TOPIC_EVENT"; var RESET_EVENT = "RESET_EVENT"; // eventHandlerService has been resetted + // used for dedupping events - could be expanded in future... + // FIXME: means that we leak memory over time (along with lots of the rest + // of the app, given we never try to reap memory yet) + var eventMap = {}; + + $rootScope.presence = {}; + var initialSyncDeferred; var reset = function() { @@ -46,16 +53,13 @@ angular.module('eventHandlerService', []) $rootScope.events = { rooms: {} // will contain roomId: { messages:[], members:{userid1: event} } }; - } + + $rootScope.presence = {}; + + eventMap = {}; + }; reset(); - // used for dedupping events - could be expanded in future... - // FIXME: means that we leak memory over time (along with lots of the rest - // of the app, given we never try to reap memory yet) - var eventMap = {}; - - $rootScope.presence = {}; - var initRoom = function(room_id) { if (!(room_id in $rootScope.events.rooms)) { console.log("Creating new handler entry for " + room_id); @@ -204,7 +208,7 @@ angular.module('eventHandlerService', []) var handleCallEvent = function(event, isLiveEvent) { $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); - if (event.type == 'm.call.invite') { + if (event.type === 'm.call.invite') { $rootScope.events.rooms[event.room_id].messages.push(event); } }; @@ -231,7 +235,7 @@ angular.module('eventHandlerService', []) } } return index; - } + }; return { ROOM_CREATE_EVENT: ROOM_CREATE_EVENT, From 76217890c01a282aa541e9005e0bdfbb7cb65cd2 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 11:14:10 +0200 Subject: [PATCH 22/23] BF: inviter field has moved to the room root object --- webclient/recents/recents.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index 3d736b669..ca7636e36 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -22,7 +22,7 @@
- {{ room.lastMsg.inviter | mUserDisplayName: room.room_id }} invited you + {{ room.inviter | mUserDisplayName: room.room_id }} invited you
From 5bd9369a62c6d6cf677e9eef7d58096449542cdf Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 15 Sep 2014 13:26:05 +0100 Subject: [PATCH 23/23] Correctly handle the 'age' key in events and pdus --- synapse/api/events/__init__.py | 13 +++++++++++++ synapse/api/events/factory.py | 8 ++++++++ synapse/federation/replication.py | 15 ++++++++++++--- synapse/handlers/events.py | 10 ++++------ synapse/handlers/message.py | 6 +++--- synapse/handlers/room.py | 2 +- synapse/rest/events.py | 2 +- synapse/rest/room.py | 2 +- synapse/server.py | 4 ++++ synapse/storage/_base.py | 4 ++++ 10 files changed, 51 insertions(+), 15 deletions(-) diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py index 5f300de10..72c493db5 100644 --- a/synapse/api/events/__init__.py +++ b/synapse/api/events/__init__.py @@ -17,6 +17,18 @@ from synapse.api.errors import SynapseError, Codes from synapse.util.jsonobject import JsonEncodedObject +def serialize_event(hs, e): + # FIXME(erikj): To handle the case of presence events and the like + if not isinstance(e, SynapseEvent): + return e + + d = e.get_dict() + if "age_ts" in d: + d["age"] = int(hs.get_clock().time_msec()) - d["age_ts"] + + return d + + class SynapseEvent(JsonEncodedObject): """Base class for Synapse events. These are JSON objects which must abide @@ -43,6 +55,7 @@ class SynapseEvent(JsonEncodedObject): "content", # HTTP body, JSON "state_key", "required_power_level", + "age_ts", ] internal_keys = [ diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py index 5e38cdbc4..d3d96d73e 100644 --- a/synapse/api/events/factory.py +++ b/synapse/api/events/factory.py @@ -59,6 +59,14 @@ class EventFactory(object): if "ts" not in kwargs: kwargs["ts"] = int(self.clock.time_msec()) + # The "age" key is a delta timestamp that should be converted into an + # absolute timestamp the minute we see it. + if "age" in kwargs: + kwargs["age_ts"] = int(self.clock.time_msec()) - int(kwargs["age"]) + del kwargs["age"] + elif "age_ts" not in kwargs: + kwargs["age_ts"] = int(self.clock.time_msec()) + if etype in self._event_list: handler = self._event_list[etype] else: diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py index e12510017..c79ce4468 100644 --- a/synapse/federation/replication.py +++ b/synapse/federation/replication.py @@ -291,6 +291,12 @@ class ReplicationLayer(object): def on_incoming_transaction(self, transaction_data): transaction = Transaction(**transaction_data) + for p in transaction.pdus: + if "age" in p: + p["age_ts"] = int(self.clock.time_msec()) - int(p["age"]) + + pdu_list = [Pdu(**p) for p in transaction.pdus] + logger.debug("[%s] Got transaction", transaction.transaction_id) response = yield self.transaction_actions.have_responded(transaction) @@ -303,8 +309,6 @@ class ReplicationLayer(object): logger.debug("[%s] Transacition is new", transaction.transaction_id) - pdu_list = [Pdu(**p) for p in transaction.pdus] - dl = [] for pdu in pdu_list: dl.append(self._handle_new_pdu(pdu)) @@ -405,9 +409,14 @@ class ReplicationLayer(object): """Returns a new Transaction containing the given PDUs suitable for transmission. """ + pdus = [p.get_dict() for p in pdu_list] + for p in pdus: + if "age_ts" in pdus: + p["age"] = int(self.clock.time_msec()) - p["age_ts"] + return Transaction( - pdus=[p.get_dict() for p in pdu_list], origin=self.server_name, + pdus=pdus, ts=int(self._clock.time_msec()), destination=None, ) diff --git a/synapse/handlers/events.py b/synapse/handlers/events.py index fd24a11fb..93dcd4032 100644 --- a/synapse/handlers/events.py +++ b/synapse/handlers/events.py @@ -15,7 +15,6 @@ from twisted.internet import defer -from synapse.api.events import SynapseEvent from synapse.util.logutils import log_function from ._base import BaseHandler @@ -71,10 +70,7 @@ class EventStreamHandler(BaseHandler): auth_user, room_ids, pagin_config, timeout ) - chunks = [ - e.get_dict() if isinstance(e, SynapseEvent) else e - for e in events - ] + chunks = [self.hs.serialize_event(e) for e in events] chunk = { "chunk": chunks, @@ -92,7 +88,9 @@ class EventStreamHandler(BaseHandler): # 10 seconds of grace to allow the client to reconnect again # before we think they're gone def _later(): - logger.debug("_later stopped_user_eventstream %s", auth_user) + logger.debug( + "_later stopped_user_eventstream %s", auth_user + ) self.distributor.fire( "stopped_user_eventstream", auth_user ) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 87fc04478..b63863e5b 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -124,7 +124,7 @@ class MessageHandler(BaseHandler): ) chunk = { - "chunk": [e.get_dict() for e in events], + "chunk": [self.hs.serialize_event(e) for e in events], "start": pagin_config.from_token.to_string(), "end": next_token.to_string(), } @@ -296,7 +296,7 @@ class MessageHandler(BaseHandler): end_token = now_token.copy_and_replace("room_key", token[1]) d["messages"] = { - "chunk": [m.get_dict() for m in messages], + "chunk": [self.hs.serialize_event(m) for m in messages], "start": start_token.to_string(), "end": end_token.to_string(), } @@ -304,7 +304,7 @@ class MessageHandler(BaseHandler): current_state = yield self.store.get_current_state( event.room_id ) - d["state"] = [c.get_dict() for c in current_state] + d["state"] = [self.hs.serialize_event(c) for c in current_state] except: logger.exception("Failed to get snapshot") diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 310cb46fe..5bc128043 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -335,7 +335,7 @@ class RoomMemberHandler(BaseHandler): member_list = yield self.store.get_room_members(room_id=room_id) event_list = [ - entry.get_dict() + self.hs.serialize_event(entry) for entry in member_list ] chunk_data = { diff --git a/synapse/rest/events.py b/synapse/rest/events.py index 7fde14320..097195d7c 100644 --- a/synapse/rest/events.py +++ b/synapse/rest/events.py @@ -59,7 +59,7 @@ class EventRestServlet(RestServlet): event = yield handler.get_event(auth_user, event_id) if event: - defer.returnValue((200, event.get_dict())) + defer.returnValue((200, self.hs.serialize_event(event))) else: defer.returnValue((404, "Event not found.")) diff --git a/synapse/rest/room.py b/synapse/rest/room.py index cef700c81..ecb1e346d 100644 --- a/synapse/rest/room.py +++ b/synapse/rest/room.py @@ -378,7 +378,7 @@ class RoomTriggerBackfill(RestServlet): handler = self.handlers.federation_handler events = yield handler.backfill(remote_server, room_id, limit) - res = [event.get_dict() for event in events] + res = [self.hs.serialize_event(event) for event in events] defer.returnValue((200, res)) diff --git a/synapse/server.py b/synapse/server.py index 83368ea5a..7c185537a 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -20,6 +20,7 @@ # Imports required for the default HomeServer() implementation from synapse.federation import initialize_http_replication +from synapse.api.events import serialize_event from synapse.api.events.factory import EventFactory from synapse.notifier import Notifier from synapse.api.auth import Auth @@ -138,6 +139,9 @@ class BaseHomeServer(object): object.""" return RoomID.from_string(s, hs=self) + def serialize_event(self, e): + return serialize_event(self, e) + # Build magic accessors for every dependency for depname in BaseHomeServer.DEPENDENCIES: BaseHomeServer._make_dependency_method(depname) diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py index 8deaaf93b..cf88bfc22 100644 --- a/synapse/storage/_base.py +++ b/synapse/storage/_base.py @@ -315,6 +315,10 @@ class SQLBaseStore(object): d["content"] = json.loads(d["content"]) del d["unrecognized_keys"] + if "age_ts" not in d: + # For compatibility + d["age_ts"] = d["ts"] if "ts" in d else 0 + return self.event_factory.create_event( etype=d["type"], **d