Skip to content
Snippets Groups Projects
Verified Commit 0f47c6a7 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Register more signals to detect changes that need a Matrix room sync

parent 6e6218e6
No related branches found
No related tags found
1 merge request!7Resolve "Add periodic task for syncing rooms"
Pipeline #64873 failed
from django.db.models.signals import post_save from django.db.models.signals import m2m_changed, post_save
from aleksis.core.util.apps import AppConfig from aleksis.core.util.apps import AppConfig
...@@ -17,6 +17,12 @@ class DefaultConfig(AppConfig): ...@@ -17,6 +17,12 @@ class DefaultConfig(AppConfig):
def ready(self): def ready(self):
from aleksis.core.models import Group from aleksis.core.models import Group
from .signals import post_save_matrix_signal from .models import MatrixProfile, MatrixRoom
from .signals import m2m_changed_matrix_signal, post_save_matrix_signal
post_save.connect(post_save_matrix_signal, sender=Group) post_save.connect(post_save_matrix_signal, sender=Group)
post_save.connect(post_save_matrix_signal, sender=MatrixProfile)
post_save.connect(post_save_matrix_signal, sender=MatrixRoom)
m2m_changed.connect(m2m_changed_matrix_signal, sender=Group.members.through)
m2m_changed.connect(m2m_changed_matrix_signal, sender=Group.owners.through)
...@@ -6,6 +6,8 @@ from django.db.models import Q, QuerySet ...@@ -6,6 +6,8 @@ from django.db.models import Q, QuerySet
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from model_utils import FieldTracker
from aleksis.core.mixins import ExtensibleModel, ExtensiblePolymorphicModel from aleksis.core.mixins import ExtensibleModel, ExtensiblePolymorphicModel
from aleksis.core.models import Group, Person from aleksis.core.models import Group, Person
from aleksis.core.util.core_helpers import get_site_preferences from aleksis.core.util.core_helpers import get_site_preferences
...@@ -26,6 +28,8 @@ class MatrixProfile(ExtensibleModel): ...@@ -26,6 +28,8 @@ class MatrixProfile(ExtensibleModel):
related_name="matrix_profile", related_name="matrix_profile",
) )
change_tracker = FieldTracker()
@classmethod @classmethod
def build_matrix_id(cls, username: str, homeserver: Optional[str] = None) -> str: def build_matrix_id(cls, username: str, homeserver: Optional[str] = None) -> str:
"""Build a Matrix ID from a username.""" """Build a Matrix ID from a username."""
...@@ -65,6 +69,8 @@ class MatrixRoom(ExtensiblePolymorphicModel): ...@@ -65,6 +69,8 @@ class MatrixRoom(ExtensiblePolymorphicModel):
related_name="matrix_rooms", related_name="matrix_rooms",
) )
change_tracker = FieldTracker(["group_id"])
@classmethod @classmethod
def get_queryset(cls): def get_queryset(cls):
"""Get a queryset for only Matrix rooms.""" """Get a queryset for only Matrix rooms."""
......
from aleksis.apps.matrix.models import MatrixRoom from django.db.models import Q
from aleksis.apps.matrix.models import MatrixProfile, MatrixRoom
from aleksis.core.models import Group
from .tasks import sync_room from .tasks import sync_room
def post_save_matrix_signal(sender, instance, created, **kwargs): def post_save_matrix_signal(sender, instance, created, **kwargs):
"""Sync Matrix room after changing a group.""" """Sync Matrix room after changing a group/Matrix room/Matrix profile."""
if created: if created:
return return
for room in MatrixRoom.objects.filter(group=instance): rooms = []
if isinstance(instance, Group):
rooms = MatrixRoom.objects.filter(group=instance)
elif isinstance(instance, MatrixRoom) and instance.change_tracker.has_changed("group_id"):
rooms = [instance]
elif isinstance(instance, MatrixProfile) and instance.change_tracker.changed():
rooms = MatrixRoom.objects.filter(
Q(group__members=instance.person) | Q(group__owners=instance.person)
).distinct()
for room in rooms:
sync_room.delay(room.pk)
def m2m_changed_matrix_signal(sender, instance, action, reverse, model, pk_set, **kwargs):
"""Sync Matrix room after changing group member- and ownerships."""
if action not in ("post_add", "post_remove", "post_clear"):
return
if isinstance(instance, Group):
groups = [instance]
else:
groups = Group.objects.filter(Q(members=instance) | Q(owners=instance)).distinct()
for room in MatrixRoom.objects.filter(group__in=groups):
sync_room.delay(room.pk) sync_room.delay(room.pk)
import time import time
from datetime import date from datetime import date
from unittest.mock import call
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Q
import pytest import pytest
import requests import requests
...@@ -318,7 +320,6 @@ def test_use_room_sync(matrix_bot_user): ...@@ -318,7 +320,6 @@ def test_use_room_sync(matrix_bot_user):
from django.test import TransactionTestCase, override_settings from django.test import TransactionTestCase, override_settings
@pytest.mark.usefixtures("celery_worker", "matrix_bot_user") @pytest.mark.usefixtures("celery_worker", "matrix_bot_user")
@override_settings(CELERY_BROKER_URL="memory://localhost//") @override_settings(CELERY_BROKER_URL="memory://localhost//")
@override_settings(HAYSTACK_SIGNAL_PROCESSOR="") @override_settings(HAYSTACK_SIGNAL_PROCESSOR="")
...@@ -497,3 +498,202 @@ def test_too_much_invites(matrix_bot_user): ...@@ -497,3 +498,202 @@ def test_too_much_invites(matrix_bot_user):
room = MatrixRoom.from_group(g) room = MatrixRoom.from_group(g)
room.sync_profiles() room.sync_profiles()
def test_signal_group_changed(matrix_bot_user, mocker):
g = Group.objects.create(name="Test Room")
room = MatrixRoom.from_group(g)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
g.name = "Test Room 2"
g.save()
sync_mock.assert_called_once_with(room.pk)
def test_signal_room_group_changed(matrix_bot_user, mocker):
g = Group.objects.create(name="Test Room")
g2 = Group.objects.create(name="Test Room 2")
room = MatrixRoom.from_group(g)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
room.group = g2
room.save()
sync_mock.assert_called_once_with(room.pk)
def test_signal_profile_person_changed(matrix_bot_user, mocker):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
p = Person.objects.create(
first_name="Test", last_name="Person", user=User.objects.create(username="test")
)
p2 = Person.objects.create(
first_name="Test 2", last_name="Person 2", user=User.objects.create(username="test2")
)
p3 = Person.objects.create(
first_name="Test 3", last_name="Person 3", user=User.objects.create(username="test3")
)
p4 = Person.objects.create(
first_name="Test 4", last_name="Person 4", user=User.objects.create(username="test4")
)
g = Group.objects.create(name="Test Room")
g.members.set([p])
g.owners.set([p3])
g2 = Group.objects.create(name="Test Room 2")
g2.members.set([p2])
g2.owners.set([p4])
room = MatrixRoom.from_group(g)
room2 = MatrixRoom.from_group(g2)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
profile = MatrixProfile.from_person(p)
p2.matrix_profile.delete()
profile.person = p2
profile.save()
sync_mock.assert_called_with(room2.pk)
profile2 = MatrixProfile.from_person(p3)
p4.matrix_profile.delete()
profile2.person = p4
profile2.save()
sync_mock.assert_called_with(room2.pk)
def test_signal_room_members_changed(matrix_bot_user, mocker):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
p = Person.objects.create(
first_name="Test", last_name="Person", user=User.objects.create(username="test")
)
p2 = Person.objects.create(
first_name="Test 2", last_name="Person 2", user=User.objects.create(username="test2")
)
p3 = Person.objects.create(
first_name="Test 3", last_name="Person 3", user=User.objects.create(username="test3")
)
p4 = Person.objects.create(
first_name="Test 4", last_name="Person 4", user=User.objects.create(username="test4")
)
g = Group.objects.create(name="Test Room")
g.members.set([p])
room = MatrixRoom.from_group(g)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
g.members.set([p2, p3])
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.members.add(p4)
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.members.remove(p2)
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.members.clear()
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
def test_signal_room_owners_changed(matrix_bot_user, mocker):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
p = Person.objects.create(
first_name="Test", last_name="Person", user=User.objects.create(username="test")
)
p2 = Person.objects.create(
first_name="Test 2", last_name="Person 2", user=User.objects.create(username="test2")
)
p3 = Person.objects.create(
first_name="Test 3", last_name="Person 3", user=User.objects.create(username="test3")
)
p4 = Person.objects.create(
first_name="Test 4", last_name="Person 4", user=User.objects.create(username="test4")
)
g = Group.objects.create(name="Test Room")
g.owners.set([p])
room = MatrixRoom.from_group(g)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
g.owners.set([p2, p3])
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.owners.add(p4)
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.owners.remove(p2)
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
g.owners.clear()
sync_mock.assert_called_with(room.pk)
sync_mock.reset_mock()
def test_signal_room_members_changed_reverse(matrix_bot_user, mocker):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
p = Person.objects.create(
first_name="Test", last_name="Person", user=User.objects.create(username="test")
)
g = Group.objects.create(name="Test Room")
g2 = Group.objects.create(name="Test Room 2")
room = MatrixRoom.from_group(g)
room2 = MatrixRoom.from_group(g2)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
p.member_of.set([g, g2])
sync_mock.assert_has_calls([call(room.pk), call(room2.pk)])
sync_mock.reset_mock()
def test_signal_room_owners_changed_reverse(matrix_bot_user, mocker):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
p = Person.objects.create(
first_name="Test", last_name="Person", user=User.objects.create(username="test")
)
g = Group.objects.create(name="Test Room")
g2 = Group.objects.create(name="Test Room 2")
room = MatrixRoom.from_group(g)
room2 = MatrixRoom.from_group(g2)
sync_mock = mocker.patch("aleksis.apps.matrix.tasks.sync_room.delay")
p.owner_of.set([g, g2])
sync_mock.assert_has_calls([call(room.pk), call(room2.pk)])
sync_mock.reset_mock()
...@@ -38,6 +38,7 @@ aleksis-core = "^2.7" ...@@ -38,6 +38,7 @@ aleksis-core = "^2.7"
aleksis-builddeps = "*" aleksis-builddeps = "*"
matrix-synapse = "^1.49.2" matrix-synapse = "^1.49.2"
pytest-xprocess = "^0.18.1" pytest-xprocess = "^0.18.1"
pytest-mock = "^3.7.0"
[tool.poetry.plugins."aleksis.app"] [tool.poetry.plugins."aleksis.app"]
matrix = "aleksis.apps.matrix.apps:DefaultConfig" matrix = "aleksis.apps.matrix.apps:DefaultConfig"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment