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.
|
# Apps specific for this project go here.
|
||||||
LOCAL_APPS = (
|
LOCAL_APPS = (
|
||||||
'funkwhale_api.common',
|
'funkwhale_api.common',
|
||||||
|
'funkwhale_api.activity.apps.ActivityConfig',
|
||||||
'funkwhale_api.users', # custom users app
|
'funkwhale_api.users', # custom users app
|
||||||
# Your stuff: custom apps go here
|
# Your stuff: custom apps go here
|
||||||
'funkwhale_api.instance',
|
'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 dynamic_preferences.registries import global_preferences_registry
|
||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from funkwhale_api.activity import record
|
||||||
from funkwhale_api.taskapp import celery
|
from funkwhale_api.taskapp import celery
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,3 +82,28 @@ def superuser_client(db, factories, client):
|
||||||
setattr(client, 'user', user)
|
setattr(client, 'user', user)
|
||||||
yield client
|
yield client
|
||||||
delattr(client, 'user')
|
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