Skip to content
Snippets Groups Projects
Commit 41d34239 authored by Nik | Klampfradler's avatar Nik | Klampfradler
Browse files

Merge branch '18-add-periodic-task-for-syncing-rooms' into 'master'

Resolve "Add periodic task for syncing rooms"

Closes #18

See merge request !7
parents c9a8e559 a85d41c5
No related branches found
No related tags found
1 merge request!7Resolve "Add periodic task for syncing rooms"
Pipeline #73356 passed with warnings
Pipeline: AlekSIS

#73361

    from django.db.models.signals import m2m_changed, post_save
    from aleksis.core.util.apps import AppConfig from aleksis.core.util.apps import AppConfig
    ...@@ -11,3 +13,16 @@ class DefaultConfig(AppConfig): ...@@ -11,3 +13,16 @@ class DefaultConfig(AppConfig):
    } }
    licence = "EUPL-1.2+" licence = "EUPL-1.2+"
    copyright_info = (([2021, 2022], "Jonathan Weth", "dev@jonathanweth.de"),) copyright_info = (([2021, 2022], "Jonathan Weth", "dev@jonathanweth.de"),)
    def ready(self):
    from aleksis.core.models import Group
    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=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 django.db.models import Q
    from aleksis.apps.matrix.models import MatrixProfile, MatrixRoom
    from aleksis.core.models import Group
    from .tasks import sync_room
    def post_save_matrix_signal(sender, instance, created, **kwargs):
    """Sync Matrix room after changing a group/Matrix room/Matrix profile."""
    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)
    from datetime import timedelta
    from typing import Sequence from typing import Sequence
    from aleksis.apps.matrix.models import MatrixRoom from aleksis.apps.matrix.models import MatrixRoom
    ...@@ -25,3 +26,11 @@ def provision_group_in_matrix(pk: int): ...@@ -25,3 +26,11 @@ def provision_group_in_matrix(pk: int):
    """Provision provided group in Matrix.""" """Provision provided group in Matrix."""
    group = Group.objects.get(pk=pk) group = Group.objects.get(pk=pk)
    group._provision_in_matrix() group._provision_in_matrix()
    @app.task(run_every=timedelta(days=1))
    def sync_rooms():
    """Synchronise all Matrix rooms."""
    rooms = MatrixRoom.objects.all()
    for room in rooms:
    sync_room.delay(room.pk)
    ...@@ -30,4 +30,6 @@ form_secret: "eYJgrzzEXHsgblxAi3pBmPsNrXrga.OVTKkmb&u64A11V_8axr" ...@@ -30,4 +30,6 @@ form_secret: "eYJgrzzEXHsgblxAi3pBmPsNrXrga.OVTKkmb&u64A11V_8axr"
    signing_key_path: "%path%/synapse/matrix.aleksis.example.org.signing.key" signing_key_path: "%path%/synapse/matrix.aleksis.example.org.signing.key"
    trusted_key_servers: trusted_key_servers:
    - server_name: "matrix.org" - server_name: "matrix.org"
    \ No newline at end of file
    enable_registration_without_verification: true
    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
    ...@@ -319,32 +321,6 @@ def test_use_room_sync(matrix_bot_user): ...@@ -319,32 +321,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")
    @override_settings(CELERY_BROKER_URL="memory://localhost//")
    @override_settings(HAYSTACK_SIGNAL_PROCESSOR="")
    class MatrixCeleryTest(TransactionTestCase):
    serialized_rollback = True
    def test_use_room_async(self):
    get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
    g = Group.objects.create(name="Test Room")
    u1 = User.objects.create_user("test1", "test1@example.org", "test1")
    p1 = Person.objects.create(first_name="Test", last_name="Person", user=u1)
    g.members.add(p1)
    r = g.provision_in_matrix(sync=False)
    assert isinstance(r, AsyncResult)
    time.sleep(3)
    assert MatrixProfile.objects.all().count() == 1
    assert p1.matrix_profile
    assert p1.matrix_profile.matrix_id == "@test1:matrix.aleksis.example.org"
    def test_space_creation(matrix_bot_user): def test_space_creation(matrix_bot_user):
    parent_group = Group.objects.create(name="Test Group") parent_group = Group.objects.create(name="Test Group")
    child_1 = Group.objects.create(name="Test Group 1") child_1 = Group.objects.create(name="Test Group 1")
    ...@@ -497,3 +473,202 @@ def test_too_much_invites(matrix_bot_user): ...@@ -497,3 +473,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.19.0" pytest-xprocess = "^0.19.0"
    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