Add `rooms.room_version` column (#6729)
This is so that we don't have to rely on pulling it out from `current_state_events` table.
This commit is contained in:
parent
d5275fc55f
commit
8df862e45d
|
@ -0,0 +1 @@
|
||||||
|
Record room versions in the `rooms` table.
|
|
@ -17,6 +17,7 @@
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Dict, Iterable
|
||||||
|
|
||||||
from prometheus_client import Counter
|
from prometheus_client import Counter
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ from synapse.api.errors import (
|
||||||
FederationDeniedError,
|
FederationDeniedError,
|
||||||
HttpResponseException,
|
HttpResponseException,
|
||||||
SynapseError,
|
SynapseError,
|
||||||
|
UnsupportedRoomVersionError,
|
||||||
)
|
)
|
||||||
from synapse.api.room_versions import (
|
from synapse.api.room_versions import (
|
||||||
KNOWN_ROOM_VERSIONS,
|
KNOWN_ROOM_VERSIONS,
|
||||||
|
@ -385,6 +387,8 @@ class FederationClient(FederationBase):
|
||||||
return res
|
return res
|
||||||
except InvalidResponseError as e:
|
except InvalidResponseError as e:
|
||||||
logger.warning("Failed to %s via %s: %s", description, destination, e)
|
logger.warning("Failed to %s via %s: %s", description, destination, e)
|
||||||
|
except UnsupportedRoomVersionError:
|
||||||
|
raise
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
if not 500 <= e.code < 600:
|
if not 500 <= e.code < 600:
|
||||||
raise e.to_synapse_error()
|
raise e.to_synapse_error()
|
||||||
|
@ -404,7 +408,13 @@ class FederationClient(FederationBase):
|
||||||
raise SynapseError(502, "Failed to %s via any server" % (description,))
|
raise SynapseError(502, "Failed to %s via any server" % (description,))
|
||||||
|
|
||||||
def make_membership_event(
|
def make_membership_event(
|
||||||
self, destinations, room_id, user_id, membership, content, params
|
self,
|
||||||
|
destinations: Iterable[str],
|
||||||
|
room_id: str,
|
||||||
|
user_id: str,
|
||||||
|
membership: str,
|
||||||
|
content: dict,
|
||||||
|
params: Dict[str, str],
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Creates an m.room.member event, with context, without participating in the room.
|
Creates an m.room.member event, with context, without participating in the room.
|
||||||
|
@ -417,21 +427,23 @@ class FederationClient(FederationBase):
|
||||||
Note that this does not append any events to any graphs.
|
Note that this does not append any events to any graphs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
destinations (Iterable[str]): Candidate homeservers which are probably
|
destinations: Candidate homeservers which are probably
|
||||||
participating in the room.
|
participating in the room.
|
||||||
room_id (str): The room in which the event will happen.
|
room_id: The room in which the event will happen.
|
||||||
user_id (str): The user whose membership is being evented.
|
user_id: The user whose membership is being evented.
|
||||||
membership (str): The "membership" property of the event. Must be
|
membership: The "membership" property of the event. Must be one of
|
||||||
one of "join" or "leave".
|
"join" or "leave".
|
||||||
content (dict): Any additional data to put into the content field
|
content: Any additional data to put into the content field of the
|
||||||
of the event.
|
event.
|
||||||
params (dict[str, str|Iterable[str]]): Query parameters to include in the
|
params: Query parameters to include in the request.
|
||||||
request.
|
|
||||||
Return:
|
Return:
|
||||||
Deferred[tuple[str, FrozenEvent, int]]: resolves to a tuple of
|
Deferred[Tuple[str, FrozenEvent, RoomVersion]]: resolves to a tuple of
|
||||||
`(origin, event, event_format)` where origin is the remote
|
`(origin, event, room_version)` where origin is the remote
|
||||||
homeserver which generated the event, and event_format is one of
|
homeserver which generated the event, and room_version is the
|
||||||
`synapse.api.room_versions.EventFormatVersions`.
|
version of the room.
|
||||||
|
|
||||||
|
Fails with a `UnsupportedRoomVersionError` if remote responds with
|
||||||
|
a room version we don't understand.
|
||||||
|
|
||||||
Fails with a ``SynapseError`` if the chosen remote server
|
Fails with a ``SynapseError`` if the chosen remote server
|
||||||
returns a 300/400 code.
|
returns a 300/400 code.
|
||||||
|
@ -453,8 +465,12 @@ class FederationClient(FederationBase):
|
||||||
|
|
||||||
# Note: If not supplied, the room version may be either v1 or v2,
|
# Note: If not supplied, the room version may be either v1 or v2,
|
||||||
# however either way the event format version will be v1.
|
# however either way the event format version will be v1.
|
||||||
room_version = ret.get("room_version", RoomVersions.V1.identifier)
|
room_version_id = ret.get("room_version", RoomVersions.V1.identifier)
|
||||||
event_format = room_version_to_event_format(room_version)
|
room_version = KNOWN_ROOM_VERSIONS.get(room_version_id)
|
||||||
|
if not room_version:
|
||||||
|
raise UnsupportedRoomVersionError()
|
||||||
|
|
||||||
|
event_format = room_version_to_event_format(room_version_id)
|
||||||
|
|
||||||
pdu_dict = ret.get("event", None)
|
pdu_dict = ret.get("event", None)
|
||||||
if not isinstance(pdu_dict, dict):
|
if not isinstance(pdu_dict, dict):
|
||||||
|
@ -478,7 +494,7 @@ class FederationClient(FederationBase):
|
||||||
event_dict=pdu_dict,
|
event_dict=pdu_dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
return (destination, ev, event_format)
|
return (destination, ev, room_version)
|
||||||
|
|
||||||
return self._try_destination_list(
|
return self._try_destination_list(
|
||||||
"make_" + membership, destinations, send_request
|
"make_" + membership, destinations, send_request
|
||||||
|
|
|
@ -44,10 +44,10 @@ from synapse.api.errors import (
|
||||||
StoreError,
|
StoreError,
|
||||||
SynapseError,
|
SynapseError,
|
||||||
)
|
)
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion, RoomVersions
|
||||||
from synapse.crypto.event_signing import compute_event_signature
|
from synapse.crypto.event_signing import compute_event_signature
|
||||||
from synapse.event_auth import auth_types_for_event
|
from synapse.event_auth import auth_types_for_event
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase, room_version_to_event_format
|
||||||
from synapse.events.snapshot import EventContext
|
from synapse.events.snapshot import EventContext
|
||||||
from synapse.events.validator import EventValidator
|
from synapse.events.validator import EventValidator
|
||||||
from synapse.logging.context import (
|
from synapse.logging.context import (
|
||||||
|
@ -703,8 +703,20 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
if not room:
|
if not room:
|
||||||
try:
|
try:
|
||||||
|
prev_state_ids = await context.get_prev_state_ids()
|
||||||
|
create_event = await self.store.get_event(
|
||||||
|
prev_state_ids[(EventTypes.Create, "")]
|
||||||
|
)
|
||||||
|
|
||||||
|
room_version_id = create_event.content.get(
|
||||||
|
"room_version", RoomVersions.V1.identifier
|
||||||
|
)
|
||||||
|
|
||||||
await self.store.store_room(
|
await self.store.store_room(
|
||||||
room_id=room_id, room_creator_user_id="", is_public=False
|
room_id=room_id,
|
||||||
|
room_creator_user_id="",
|
||||||
|
is_public=False,
|
||||||
|
room_version=KNOWN_ROOM_VERSIONS[room_version_id],
|
||||||
)
|
)
|
||||||
except StoreError:
|
except StoreError:
|
||||||
logger.exception("Failed to store room.")
|
logger.exception("Failed to store room.")
|
||||||
|
@ -1186,7 +1198,7 @@ class FederationHandler(BaseHandler):
|
||||||
"""
|
"""
|
||||||
logger.debug("Joining %s to %s", joinee, room_id)
|
logger.debug("Joining %s to %s", joinee, room_id)
|
||||||
|
|
||||||
origin, event, event_format_version = yield self._make_and_verify_event(
|
origin, event, room_version = yield self._make_and_verify_event(
|
||||||
target_hosts,
|
target_hosts,
|
||||||
room_id,
|
room_id,
|
||||||
joinee,
|
joinee,
|
||||||
|
@ -1214,6 +1226,8 @@ class FederationHandler(BaseHandler):
|
||||||
target_hosts.insert(0, origin)
|
target_hosts.insert(0, origin)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
event_format_version = room_version_to_event_format(room_version.identifier)
|
||||||
ret = yield self.federation_client.send_join(
|
ret = yield self.federation_client.send_join(
|
||||||
target_hosts, event, event_format_version
|
target_hosts, event, event_format_version
|
||||||
)
|
)
|
||||||
|
@ -1234,13 +1248,18 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield self.store.store_room(
|
yield self.store.store_room(
|
||||||
room_id=room_id, room_creator_user_id="", is_public=False
|
room_id=room_id,
|
||||||
|
room_creator_user_id="",
|
||||||
|
is_public=False,
|
||||||
|
room_version=room_version,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
# FIXME
|
# FIXME
|
||||||
pass
|
pass
|
||||||
|
|
||||||
yield self._persist_auth_tree(origin, auth_chain, state, event)
|
yield self._persist_auth_tree(
|
||||||
|
origin, auth_chain, state, event, room_version
|
||||||
|
)
|
||||||
|
|
||||||
# Check whether this room is the result of an upgrade of a room we already know
|
# Check whether this room is the result of an upgrade of a room we already know
|
||||||
# about. If so, migrate over user information
|
# about. If so, migrate over user information
|
||||||
|
@ -1486,7 +1505,7 @@ class FederationHandler(BaseHandler):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def do_remotely_reject_invite(self, target_hosts, room_id, user_id, content):
|
def do_remotely_reject_invite(self, target_hosts, room_id, user_id, content):
|
||||||
origin, event, event_format_version = yield self._make_and_verify_event(
|
origin, event, room_version = yield self._make_and_verify_event(
|
||||||
target_hosts, room_id, user_id, "leave", content=content
|
target_hosts, room_id, user_id, "leave", content=content
|
||||||
)
|
)
|
||||||
# Mark as outlier as we don't have any state for this event; we're not
|
# Mark as outlier as we don't have any state for this event; we're not
|
||||||
|
@ -1513,7 +1532,11 @@ class FederationHandler(BaseHandler):
|
||||||
def _make_and_verify_event(
|
def _make_and_verify_event(
|
||||||
self, target_hosts, room_id, user_id, membership, content={}, params=None
|
self, target_hosts, room_id, user_id, membership, content={}, params=None
|
||||||
):
|
):
|
||||||
origin, event, format_ver = yield self.federation_client.make_membership_event(
|
(
|
||||||
|
origin,
|
||||||
|
event,
|
||||||
|
room_version,
|
||||||
|
) = yield self.federation_client.make_membership_event(
|
||||||
target_hosts, room_id, user_id, membership, content, params=params
|
target_hosts, room_id, user_id, membership, content, params=params
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1525,7 +1548,7 @@ class FederationHandler(BaseHandler):
|
||||||
assert event.user_id == user_id
|
assert event.user_id == user_id
|
||||||
assert event.state_key == user_id
|
assert event.state_key == user_id
|
||||||
assert event.room_id == room_id
|
assert event.room_id == room_id
|
||||||
return origin, event, format_ver
|
return origin, event, room_version
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
|
@ -1810,7 +1833,14 @@ class FederationHandler(BaseHandler):
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _persist_auth_tree(self, origin, auth_events, state, event):
|
def _persist_auth_tree(
|
||||||
|
self,
|
||||||
|
origin: str,
|
||||||
|
auth_events: List[EventBase],
|
||||||
|
state: List[EventBase],
|
||||||
|
event: EventBase,
|
||||||
|
room_version: RoomVersion,
|
||||||
|
):
|
||||||
"""Checks the auth chain is valid (and passes auth checks) for the
|
"""Checks the auth chain is valid (and passes auth checks) for the
|
||||||
state and event. Then persists the auth chain and state atomically.
|
state and event. Then persists the auth chain and state atomically.
|
||||||
Persists the event separately. Notifies about the persisted events
|
Persists the event separately. Notifies about the persisted events
|
||||||
|
@ -1819,10 +1849,12 @@ class FederationHandler(BaseHandler):
|
||||||
Will attempt to fetch missing auth events.
|
Will attempt to fetch missing auth events.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
origin (str): Where the events came from
|
origin: Where the events came from
|
||||||
auth_events (list)
|
auth_events
|
||||||
state (list)
|
state
|
||||||
event (Event)
|
event
|
||||||
|
room_version: The room version we expect this room to have, and
|
||||||
|
will raise if it doesn't match the version in the create event.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred
|
Deferred
|
||||||
|
@ -1848,10 +1880,13 @@ class FederationHandler(BaseHandler):
|
||||||
# invalid, and it would fail auth checks anyway.
|
# invalid, and it would fail auth checks anyway.
|
||||||
raise SynapseError(400, "No create event in state")
|
raise SynapseError(400, "No create event in state")
|
||||||
|
|
||||||
room_version = create_event.content.get(
|
room_version_id = create_event.content.get(
|
||||||
"room_version", RoomVersions.V1.identifier
|
"room_version", RoomVersions.V1.identifier
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if room_version.identifier != room_version_id:
|
||||||
|
raise SynapseError(400, "Room version mismatch")
|
||||||
|
|
||||||
missing_auth_events = set()
|
missing_auth_events = set()
|
||||||
for e in itertools.chain(auth_events, state, [event]):
|
for e in itertools.chain(auth_events, state, [event]):
|
||||||
for e_id in e.auth_event_ids():
|
for e_id in e.auth_event_ids():
|
||||||
|
|
|
@ -29,7 +29,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
|
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
|
||||||
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
|
from synapse.api.errors import AuthError, Codes, NotFoundError, StoreError, SynapseError
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion
|
||||||
from synapse.http.endpoint import parse_and_validate_server_name
|
from synapse.http.endpoint import parse_and_validate_server_name
|
||||||
from synapse.storage.state import StateFilter
|
from synapse.storage.state import StateFilter
|
||||||
from synapse.types import (
|
from synapse.types import (
|
||||||
|
@ -100,13 +100,15 @@ class RoomCreationHandler(BaseHandler):
|
||||||
self.third_party_event_rules = hs.get_third_party_event_rules()
|
self.third_party_event_rules = hs.get_third_party_event_rules()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def upgrade_room(self, requester, old_room_id, new_version):
|
def upgrade_room(
|
||||||
|
self, requester: Requester, old_room_id: str, new_version: RoomVersion
|
||||||
|
):
|
||||||
"""Replace a room with a new room with a different version
|
"""Replace a room with a new room with a different version
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
requester (synapse.types.Requester): the user requesting the upgrade
|
requester: the user requesting the upgrade
|
||||||
old_room_id (unicode): the id of the room to be replaced
|
old_room_id: the id of the room to be replaced
|
||||||
new_version (unicode): the new room version to use
|
new_version: the new room version to use
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred[unicode]: the new room id
|
Deferred[unicode]: the new room id
|
||||||
|
@ -151,7 +153,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
if r is None:
|
if r is None:
|
||||||
raise NotFoundError("Unknown room id %s" % (old_room_id,))
|
raise NotFoundError("Unknown room id %s" % (old_room_id,))
|
||||||
new_room_id = yield self._generate_room_id(
|
new_room_id = yield self._generate_room_id(
|
||||||
creator_id=user_id, is_public=r["is_public"]
|
creator_id=user_id, is_public=r["is_public"], room_version=new_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("Creating new room %s to replace %s", new_room_id, old_room_id)
|
logger.info("Creating new room %s to replace %s", new_room_id, old_room_id)
|
||||||
|
@ -299,18 +301,22 @@ class RoomCreationHandler(BaseHandler):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def clone_existing_room(
|
def clone_existing_room(
|
||||||
self, requester, old_room_id, new_room_id, new_room_version, tombstone_event_id
|
self,
|
||||||
|
requester: Requester,
|
||||||
|
old_room_id: str,
|
||||||
|
new_room_id: str,
|
||||||
|
new_room_version: RoomVersion,
|
||||||
|
tombstone_event_id: str,
|
||||||
):
|
):
|
||||||
"""Populate a new room based on an old room
|
"""Populate a new room based on an old room
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
requester (synapse.types.Requester): the user requesting the upgrade
|
requester: the user requesting the upgrade
|
||||||
old_room_id (unicode): the id of the room to be replaced
|
old_room_id : the id of the room to be replaced
|
||||||
new_room_id (unicode): the id to give the new room (should already have been
|
new_room_id: the id to give the new room (should already have been
|
||||||
created with _gemerate_room_id())
|
created with _gemerate_room_id())
|
||||||
new_room_version (unicode): the new room version to use
|
new_room_version: the new room version to use
|
||||||
tombstone_event_id (unicode|str): the ID of the tombstone event in the old
|
tombstone_event_id: the ID of the tombstone event in the old room.
|
||||||
room.
|
|
||||||
Returns:
|
Returns:
|
||||||
Deferred
|
Deferred
|
||||||
"""
|
"""
|
||||||
|
@ -320,7 +326,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
raise SynapseError(403, "You are not permitted to create rooms")
|
raise SynapseError(403, "You are not permitted to create rooms")
|
||||||
|
|
||||||
creation_content = {
|
creation_content = {
|
||||||
"room_version": new_room_version,
|
"room_version": new_room_version.identifier,
|
||||||
"predecessor": {"room_id": old_room_id, "event_id": tombstone_event_id},
|
"predecessor": {"room_id": old_room_id, "event_id": tombstone_event_id},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,14 +583,15 @@ class RoomCreationHandler(BaseHandler):
|
||||||
if ratelimit:
|
if ratelimit:
|
||||||
yield self.ratelimit(requester)
|
yield self.ratelimit(requester)
|
||||||
|
|
||||||
room_version = config.get(
|
room_version_id = config.get(
|
||||||
"room_version", self.config.default_room_version.identifier
|
"room_version", self.config.default_room_version.identifier
|
||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(room_version, string_types):
|
if not isinstance(room_version_id, string_types):
|
||||||
raise SynapseError(400, "room_version must be a string", Codes.BAD_JSON)
|
raise SynapseError(400, "room_version must be a string", Codes.BAD_JSON)
|
||||||
|
|
||||||
if room_version not in KNOWN_ROOM_VERSIONS:
|
room_version = KNOWN_ROOM_VERSIONS.get(room_version_id)
|
||||||
|
if room_version is None:
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
400,
|
400,
|
||||||
"Your homeserver does not support this room version",
|
"Your homeserver does not support this room version",
|
||||||
|
@ -631,7 +638,9 @@ class RoomCreationHandler(BaseHandler):
|
||||||
visibility = config.get("visibility", None)
|
visibility = config.get("visibility", None)
|
||||||
is_public = visibility == "public"
|
is_public = visibility == "public"
|
||||||
|
|
||||||
room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public)
|
room_id = yield self._generate_room_id(
|
||||||
|
creator_id=user_id, is_public=is_public, room_version=room_version,
|
||||||
|
)
|
||||||
|
|
||||||
directory_handler = self.hs.get_handlers().directory_handler
|
directory_handler = self.hs.get_handlers().directory_handler
|
||||||
if room_alias:
|
if room_alias:
|
||||||
|
@ -660,7 +669,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
creation_content = config.get("creation_content", {})
|
creation_content = config.get("creation_content", {})
|
||||||
|
|
||||||
# override any attempt to set room versions via the creation_content
|
# override any attempt to set room versions via the creation_content
|
||||||
creation_content["room_version"] = room_version
|
creation_content["room_version"] = room_version.identifier
|
||||||
|
|
||||||
yield self._send_events_for_new_room(
|
yield self._send_events_for_new_room(
|
||||||
requester,
|
requester,
|
||||||
|
@ -849,7 +858,9 @@ class RoomCreationHandler(BaseHandler):
|
||||||
yield send(etype=etype, state_key=state_key, content=content)
|
yield send(etype=etype, state_key=state_key, content=content)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _generate_room_id(self, creator_id, is_public):
|
def _generate_room_id(
|
||||||
|
self, creator_id: str, is_public: str, room_version: RoomVersion,
|
||||||
|
):
|
||||||
# autogen room IDs and try to create it. We may clash, so just
|
# autogen room IDs and try to create it. We may clash, so just
|
||||||
# try a few times till one goes through, giving up eventually.
|
# try a few times till one goes through, giving up eventually.
|
||||||
attempts = 0
|
attempts = 0
|
||||||
|
@ -863,6 +874,7 @@ class RoomCreationHandler(BaseHandler):
|
||||||
room_id=gen_room_id,
|
room_id=gen_room_id,
|
||||||
room_creator_user_id=creator_id,
|
room_creator_user_id=creator_id,
|
||||||
is_public=is_public,
|
is_public=is_public,
|
||||||
|
room_version=room_version,
|
||||||
)
|
)
|
||||||
return gen_room_id
|
return gen_room_id
|
||||||
except StoreError:
|
except StoreError:
|
||||||
|
|
|
@ -64,7 +64,8 @@ class RoomUpgradeRestServlet(RestServlet):
|
||||||
assert_params_in_dict(content, ("new_version",))
|
assert_params_in_dict(content, ("new_version",))
|
||||||
new_version = content["new_version"]
|
new_version = content["new_version"]
|
||||||
|
|
||||||
if new_version not in KNOWN_ROOM_VERSIONS:
|
new_version = KNOWN_ROOM_VERSIONS.get(content["new_version"])
|
||||||
|
if new_version is None:
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
400,
|
400,
|
||||||
"Your homeserver does not support this room version",
|
"Your homeserver does not support this room version",
|
||||||
|
|
|
@ -29,9 +29,10 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes
|
||||||
from synapse.api.errors import StoreError
|
from synapse.api.errors import StoreError
|
||||||
|
from synapse.api.room_versions import RoomVersion, RoomVersions
|
||||||
from synapse.storage._base import SQLBaseStore
|
from synapse.storage._base import SQLBaseStore
|
||||||
from synapse.storage.data_stores.main.search import SearchStore
|
from synapse.storage.data_stores.main.search import SearchStore
|
||||||
from synapse.storage.database import Database
|
from synapse.storage.database import Database, LoggingTransaction
|
||||||
from synapse.types import ThirdPartyInstanceID
|
from synapse.types import ThirdPartyInstanceID
|
||||||
from synapse.util.caches.descriptors import cached, cachedInlineCallbacks
|
from synapse.util.caches.descriptors import cached, cachedInlineCallbacks
|
||||||
|
|
||||||
|
@ -734,6 +735,7 @@ class RoomWorkerStore(SQLBaseStore):
|
||||||
|
|
||||||
class RoomBackgroundUpdateStore(SQLBaseStore):
|
class RoomBackgroundUpdateStore(SQLBaseStore):
|
||||||
REMOVE_TOMESTONED_ROOMS_BG_UPDATE = "remove_tombstoned_rooms_from_directory"
|
REMOVE_TOMESTONED_ROOMS_BG_UPDATE = "remove_tombstoned_rooms_from_directory"
|
||||||
|
ADD_ROOMS_ROOM_VERSION_COLUMN = "add_rooms_room_version_column"
|
||||||
|
|
||||||
def __init__(self, database: Database, db_conn, hs):
|
def __init__(self, database: Database, db_conn, hs):
|
||||||
super(RoomBackgroundUpdateStore, self).__init__(database, db_conn, hs)
|
super(RoomBackgroundUpdateStore, self).__init__(database, db_conn, hs)
|
||||||
|
@ -749,6 +751,11 @@ class RoomBackgroundUpdateStore(SQLBaseStore):
|
||||||
self._remove_tombstoned_rooms_from_directory,
|
self._remove_tombstoned_rooms_from_directory,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.db.updates.register_background_update_handler(
|
||||||
|
self.ADD_ROOMS_ROOM_VERSION_COLUMN,
|
||||||
|
self._background_add_rooms_room_version_column,
|
||||||
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _background_insert_retention(self, progress, batch_size):
|
def _background_insert_retention(self, progress, batch_size):
|
||||||
"""Retrieves a list of all rooms within a range and inserts an entry for each of
|
"""Retrieves a list of all rooms within a range and inserts an entry for each of
|
||||||
|
@ -817,6 +824,73 @@ class RoomBackgroundUpdateStore(SQLBaseStore):
|
||||||
|
|
||||||
defer.returnValue(batch_size)
|
defer.returnValue(batch_size)
|
||||||
|
|
||||||
|
async def _background_add_rooms_room_version_column(
|
||||||
|
self, progress: dict, batch_size: int
|
||||||
|
):
|
||||||
|
"""Background update to go and add room version inforamtion to `rooms`
|
||||||
|
table from `current_state_events` table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
last_room_id = progress.get("room_id", "")
|
||||||
|
|
||||||
|
def _background_add_rooms_room_version_column_txn(txn: LoggingTransaction):
|
||||||
|
sql = """
|
||||||
|
SELECT room_id, json FROM current_state_events
|
||||||
|
INNER JOIN event_json USING (room_id, event_id)
|
||||||
|
WHERE room_id > ? AND type = 'm.room.create' AND state_key = ''
|
||||||
|
ORDER BY room_id
|
||||||
|
LIMIT ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
txn.execute(sql, (last_room_id, batch_size))
|
||||||
|
|
||||||
|
updates = []
|
||||||
|
for room_id, event_json in txn:
|
||||||
|
event_dict = json.loads(event_json)
|
||||||
|
room_version_id = event_dict.get("content", {}).get(
|
||||||
|
"room_version", RoomVersions.V1.identifier
|
||||||
|
)
|
||||||
|
|
||||||
|
creator = event_dict.get("content").get("creator")
|
||||||
|
|
||||||
|
updates.append((room_id, creator, room_version_id))
|
||||||
|
|
||||||
|
if not updates:
|
||||||
|
return True
|
||||||
|
|
||||||
|
new_last_room_id = ""
|
||||||
|
for room_id, creator, room_version_id in updates:
|
||||||
|
# We upsert here just in case we don't already have a row,
|
||||||
|
# mainly for paranoia as much badness would happen if we don't
|
||||||
|
# insert the row and then try and get the room version for the
|
||||||
|
# room.
|
||||||
|
self.db.simple_upsert_txn(
|
||||||
|
txn,
|
||||||
|
table="rooms",
|
||||||
|
keyvalues={"room_id": room_id},
|
||||||
|
values={"room_version": room_version_id},
|
||||||
|
insertion_values={"is_public": False, "creator": creator},
|
||||||
|
)
|
||||||
|
new_last_room_id = room_id
|
||||||
|
|
||||||
|
self.db.updates._background_update_progress_txn(
|
||||||
|
txn, self.ADD_ROOMS_ROOM_VERSION_COLUMN, {"room_id": new_last_room_id}
|
||||||
|
)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
end = await self.db.runInteraction(
|
||||||
|
"_background_add_rooms_room_version_column",
|
||||||
|
_background_add_rooms_room_version_column_txn,
|
||||||
|
)
|
||||||
|
|
||||||
|
if end:
|
||||||
|
await self.db.updates._end_background_update(
|
||||||
|
self.ADD_ROOMS_ROOM_VERSION_COLUMN
|
||||||
|
)
|
||||||
|
|
||||||
|
return batch_size
|
||||||
|
|
||||||
async def _remove_tombstoned_rooms_from_directory(
|
async def _remove_tombstoned_rooms_from_directory(
|
||||||
self, progress, batch_size
|
self, progress, batch_size
|
||||||
) -> int:
|
) -> int:
|
||||||
|
@ -881,14 +955,21 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
|
||||||
self.config = hs.config
|
self.config = hs.config
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def store_room(self, room_id, room_creator_user_id, is_public):
|
def store_room(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
room_creator_user_id: str,
|
||||||
|
is_public: bool,
|
||||||
|
room_version: RoomVersion,
|
||||||
|
):
|
||||||
"""Stores a room.
|
"""Stores a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
room_id (str): The desired room ID, can be None.
|
room_id: The desired room ID, can be None.
|
||||||
room_creator_user_id (str): The user ID of the room creator.
|
room_creator_user_id: The user ID of the room creator.
|
||||||
is_public (bool): True to indicate that this room should appear in
|
is_public: True to indicate that this room should appear in
|
||||||
public room lists.
|
public room lists.
|
||||||
|
room_version: The version of the room
|
||||||
Raises:
|
Raises:
|
||||||
StoreError if the room could not be stored.
|
StoreError if the room could not be stored.
|
||||||
"""
|
"""
|
||||||
|
@ -902,6 +983,7 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
|
||||||
"room_id": room_id,
|
"room_id": room_id,
|
||||||
"creator": room_creator_user_id,
|
"creator": room_creator_user_id,
|
||||||
"is_public": is_public,
|
"is_public": is_public,
|
||||||
|
"room_version": room_version.identifier,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if is_public:
|
if is_public:
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/* Copyright 2020 The Matrix.org Foundation C.I.C
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
-- We want to start storing the room version independently of
|
||||||
|
-- `current_state_events` so that we can delete stale entries from it without
|
||||||
|
-- losing the information.
|
||||||
|
ALTER TABLE rooms ADD COLUMN room_version TEXT;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT into background_updates (update_name, progress_json)
|
||||||
|
VALUES ('add_rooms_room_version_column', '{}');
|
|
@ -60,24 +60,34 @@ class StateGroupWorkerStore(EventsWorkerStore, SQLBaseStore):
|
||||||
def __init__(self, database: Database, db_conn, hs):
|
def __init__(self, database: Database, db_conn, hs):
|
||||||
super(StateGroupWorkerStore, self).__init__(database, db_conn, hs)
|
super(StateGroupWorkerStore, self).__init__(database, db_conn, hs)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@cached(max_entries=10000)
|
||||||
def get_room_version(self, room_id):
|
async def get_room_version(self, room_id: str) -> str:
|
||||||
"""Get the room_version of a given room
|
"""Get the room_version of a given room
|
||||||
|
|
||||||
Args:
|
|
||||||
room_id (str)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Deferred[str]
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
NotFoundError if the room is unknown
|
NotFoundError: if the room is unknown
|
||||||
"""
|
"""
|
||||||
# for now we do this by looking at the create event. We may want to cache this
|
|
||||||
# more intelligently in future.
|
# First we try looking up room version from the database, but for old
|
||||||
|
# rooms we might not have added the room version to it yet so we fall
|
||||||
|
# back to previous behaviour and look in current state events.
|
||||||
|
|
||||||
|
# We really should have an entry in the rooms table for every room we
|
||||||
|
# care about, but let's be a bit paranoid (at least while the background
|
||||||
|
# update is happening) to avoid breaking existing rooms.
|
||||||
|
version = await self.db.simple_select_one_onecol(
|
||||||
|
table="rooms",
|
||||||
|
keyvalues={"room_id": room_id},
|
||||||
|
retcol="room_version",
|
||||||
|
desc="get_room_version",
|
||||||
|
allow_none=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if version is not None:
|
||||||
|
return version
|
||||||
|
|
||||||
# Retrieve the room's create event
|
# Retrieve the room's create event
|
||||||
create_event = yield self.get_create_event_for_room(room_id)
|
create_event = await self.get_create_event_for_room(room_id)
|
||||||
return create_event.content.get("room_version", "1")
|
return create_event.content.get("room_version", "1")
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes
|
||||||
|
from synapse.api.room_versions import RoomVersions
|
||||||
from synapse.types import RoomAlias, RoomID, UserID
|
from synapse.types import RoomAlias, RoomID, UserID
|
||||||
|
|
||||||
from tests import unittest
|
from tests import unittest
|
||||||
|
@ -40,6 +41,7 @@ class RoomStoreTestCase(unittest.TestCase):
|
||||||
self.room.to_string(),
|
self.room.to_string(),
|
||||||
room_creator_user_id=self.u_creator.to_string(),
|
room_creator_user_id=self.u_creator.to_string(),
|
||||||
is_public=True,
|
is_public=True,
|
||||||
|
room_version=RoomVersions.V1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -68,7 +70,10 @@ class RoomEventsStoreTestCase(unittest.TestCase):
|
||||||
self.room = RoomID.from_string("!abcde:test")
|
self.room = RoomID.from_string("!abcde:test")
|
||||||
|
|
||||||
yield self.store.store_room(
|
yield self.store.store_room(
|
||||||
self.room.to_string(), room_creator_user_id="@creator:text", is_public=True
|
self.room.to_string(),
|
||||||
|
room_creator_user_id="@creator:text",
|
||||||
|
is_public=True,
|
||||||
|
room_version=RoomVersions.V1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -45,7 +45,10 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
||||||
self.room = RoomID.from_string("!abc123:test")
|
self.room = RoomID.from_string("!abc123:test")
|
||||||
|
|
||||||
yield self.store.store_room(
|
yield self.store.store_room(
|
||||||
self.room.to_string(), room_creator_user_id="@creator:text", is_public=True
|
self.room.to_string(),
|
||||||
|
room_creator_user_id="@creator:text",
|
||||||
|
is_public=True,
|
||||||
|
room_version=RoomVersions.V1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -639,9 +639,17 @@ def create_room(hs, room_id, creator_id):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
persistence_store = hs.get_storage().persistence
|
persistence_store = hs.get_storage().persistence
|
||||||
|
store = hs.get_datastore()
|
||||||
event_builder_factory = hs.get_event_builder_factory()
|
event_builder_factory = hs.get_event_builder_factory()
|
||||||
event_creation_handler = hs.get_event_creation_handler()
|
event_creation_handler = hs.get_event_creation_handler()
|
||||||
|
|
||||||
|
yield store.store_room(
|
||||||
|
room_id=room_id,
|
||||||
|
room_creator_user_id=creator_id,
|
||||||
|
is_public=False,
|
||||||
|
room_version=RoomVersions.V1,
|
||||||
|
)
|
||||||
|
|
||||||
builder = event_builder_factory.for_room_version(
|
builder = event_builder_factory.for_room_version(
|
||||||
RoomVersions.V1,
|
RoomVersions.V1,
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue