Founndations for real-time event serialization/subscription/broadcasting
This commit is contained in:
parent
fd7c1e5dd8
commit
dd5881f2c6
|
@ -83,6 +83,7 @@ if RAVEN_ENABLED:
|
|||
# Apps specific for this project go here.
|
||||
LOCAL_APPS = (
|
||||
'funkwhale_api.common',
|
||||
'funkwhale_api.activity.apps.ActivityConfig',
|
||||
'funkwhale_api.users', # custom users app
|
||||
# Your stuff: custom apps go here
|
||||
'funkwhale_api.instance',
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from django.apps import AppConfig, apps
|
||||
|
||||
from . import record
|
||||
|
||||
class ActivityConfig(AppConfig):
|
||||
name = 'funkwhale_api.activity'
|
||||
|
||||
def ready(self):
|
||||
super(ActivityConfig, self).ready()
|
||||
|
||||
app_names = [app.name for app in apps.app_configs.values()]
|
||||
record.registry.autodiscover(app_names)
|
|
@ -0,0 +1,38 @@
|
|||
import persisting_theory
|
||||
|
||||
|
||||
class ActivityRegistry(persisting_theory.Registry):
|
||||
look_into = 'activities'
|
||||
|
||||
def _register_for_model(self, model, attr, value):
|
||||
key = model._meta.label
|
||||
d = self.setdefault(key, {'consumers': []})
|
||||
d[attr] = value
|
||||
|
||||
def register_serializer(self, serializer_class):
|
||||
model = serializer_class.Meta.model
|
||||
self._register_for_model(model, 'serializer', serializer_class)
|
||||
return serializer_class
|
||||
|
||||
def register_consumer(self, label):
|
||||
def decorator(func):
|
||||
consumers = self[label]['consumers']
|
||||
if func not in consumers:
|
||||
consumers.append(func)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
registry = ActivityRegistry()
|
||||
|
||||
|
||||
|
||||
|
||||
def send(obj):
|
||||
conf = registry[obj.__class__._meta.label]
|
||||
consumers = conf['consumers']
|
||||
if not consumers:
|
||||
return
|
||||
serializer = conf['serializer'](obj)
|
||||
for consumer in consumers:
|
||||
consumer(data=serializer.data, obj=obj)
|
|
@ -0,0 +1,5 @@
|
|||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
group_send = async_to_sync(channel_layer.group_send)
|
|
@ -0,0 +1,45 @@
|
|||
import pytest
|
||||
|
||||
from django.db import models
|
||||
from rest_framework import serializers
|
||||
|
||||
from funkwhale_api.activity import record
|
||||
|
||||
|
||||
class FakeModel(models.Model):
|
||||
class Meta:
|
||||
app_label = 'tests'
|
||||
|
||||
|
||||
class FakeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = FakeModel
|
||||
fields = ['id']
|
||||
|
||||
|
||||
|
||||
|
||||
def test_can_bind_serializer_to_model(activity_registry):
|
||||
activity_registry.register_serializer(FakeSerializer)
|
||||
|
||||
assert activity_registry['tests.FakeModel']['serializer'] == FakeSerializer
|
||||
|
||||
|
||||
def test_can_bind_consumer_to_model(activity_registry):
|
||||
activity_registry.register_serializer(FakeSerializer)
|
||||
@activity_registry.register_consumer('tests.FakeModel')
|
||||
def propagate(data, obj):
|
||||
return True
|
||||
|
||||
assert activity_registry['tests.FakeModel']['consumers'] == [propagate]
|
||||
|
||||
|
||||
def test_record_object_calls_consumer(activity_registry, mocker):
|
||||
activity_registry.register_serializer(FakeSerializer)
|
||||
stub = mocker.stub()
|
||||
activity_registry.register_consumer('tests.FakeModel')(stub)
|
||||
o = FakeModel(id=1)
|
||||
data = FakeSerializer(o).data
|
||||
record.send(o)
|
||||
|
||||
stub.assert_called_once_with(data=data, obj=o)
|
|
@ -5,6 +5,7 @@ from django.core.cache import cache as django_cache
|
|||
from dynamic_preferences.registries import global_preferences_registry
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from funkwhale_api.activity import record
|
||||
from funkwhale_api.taskapp import celery
|
||||
|
||||
|
||||
|
@ -81,3 +82,28 @@ def superuser_client(db, factories, client):
|
|||
setattr(client, 'user', user)
|
||||
yield client
|
||||
delattr(client, 'user')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def activity_registry():
|
||||
r = record.registry
|
||||
state = list(record.registry.items())
|
||||
yield record.registry
|
||||
record.registry.clear()
|
||||
for key, value in state:
|
||||
record.registry[key] = value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def activity_registry():
|
||||
r = record.registry
|
||||
state = list(record.registry.items())
|
||||
yield record.registry
|
||||
record.registry.clear()
|
||||
for key, value in state:
|
||||
record.registry[key] = value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def activity_muted(activity_registry, mocker):
|
||||
yield mocker.patch.object(record, 'send')
|
||||
|
|
Loading…
Reference in New Issue