diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 596662e84..93af0d0c7 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -47,8 +47,8 @@ def naive_parse(activity_objects, activity_json): print(activity_type) serializer = activity_objects[activity_type] print(serializer) - except KeyError: - raise ActivitySerializerError('Invalid type "%s"' % activity_type) + except KeyError as e: + raise ActivitySerializerError(e) return serializer(activity_objects=activity_objects, **activity_json) @@ -66,7 +66,11 @@ class ActivityObject: try: print(field.name, field.type) value = kwargs[field.name] - if field.type == 'ActivityObject' and activity_objects: + try: + is_subclass = issubclass(field.type, ActivityObject) + except TypeError: + is_subclass = False + if is_subclass and activity_objects: value = naive_parse(activity_objects, value) except KeyError: @@ -78,22 +82,17 @@ class ActivityObject: setattr(self, field.name, value) - def to_model(self, model, instance=None, save=True): + def to_model(self, instance=None, save=True): ''' convert from an activity to a model instance ''' - if self.type != model.activity_serializer.type: + # figure out the right model -- wish I had a better way for this + models = apps.get_models() + model = [m for m in models if hasattr(m, 'activity_serializer') and \ + hasattr(m.activity_serializer, 'type') and \ + m.activity_serializer.type == self.type] + if not len(model): raise ActivitySerializerError( - 'Wrong activity type "%s" for activity of type "%s"' % \ - (model.activity_serializer.type, - self.type) - ) - - if not isinstance(self, model.activity_serializer): - raise ActivitySerializerError( - 'Wrong activity type "%s" for model "%s" (expects "%s")' % \ - (self.__class__, - model.__name__, - model.activity_serializer) - ) + 'No model found for activity type "%s"' % self.type) + model = model[0] if hasattr(model, 'ignore_activity') and model.ignore_activity(self): return instance diff --git a/bookwyrm/activitypub/verbs.py b/bookwyrm/activitypub/verbs.py index 190cd7395..c4b302535 100644 --- a/bookwyrm/activitypub/verbs.py +++ b/bookwyrm/activitypub/verbs.py @@ -21,6 +21,11 @@ class Create(Verb): signature: Signature = None type: str = 'Create' + def action(self): + ''' create the model instance from the dataclass ''' + # check for dupes + self.object.to_model() + @dataclass(init=False) class Delete(Verb): @@ -46,11 +51,13 @@ class Undo(Verb): @dataclass(init=False) class Follow(Verb): ''' Follow activity ''' + object: str type: str = 'Follow' @dataclass(init=False) class Block(Verb): ''' Block activity ''' + object: str type: str = 'Block' @dataclass(init=False) diff --git a/bookwyrm/tests/views/test_inbox.py b/bookwyrm/tests/views/test_inbox.py index 64dcc149b..3bb7760e9 100644 --- a/bookwyrm/tests/views/test_inbox.py +++ b/bookwyrm/tests/views/test_inbox.py @@ -87,16 +87,41 @@ class Inbox(TestCase): def test_inbox_success(self): ''' a known type, for which we start a task ''' activity = { - "id": "hi", - "type": "Accept", - "actor": "https://example.com/users/rat", - "object": "https://example.com/user/mouse" + 'id': 'hi', + 'type': 'Create', + 'actor': 'hi', + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.com/user/mouse/followers" + ], + 'object': { + "id": "https://example.com/list/22", + "type": "BookList", + "totalItems": 1, + "first": "https://example.com/list/22?page=1", + "last": "https://example.com/list/22?page=1", + "name": "Test List", + "owner": "https://example.com/user/mouse", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.com/user/mouse/followers" + ], + "summary": "summary text", + "curation": "curated", + "@context": "https://www.w3.org/ns/activitystreams" + } } with patch('bookwyrm.views.inbox.has_valid_signature') as mock_valid: mock_valid.return_value = True - result = self.client.post( - '/inbox', - json.dumps(activity), - content_type="application/json" - ) + + with patch('bookwyrm.views.inbox.activity_task.delay'): + result = self.client.post( + '/inbox', + json.dumps(activity), + content_type="application/json" + ) self.assertEqual(result.status_code, 200) diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index 53dd6c40f..fdac84466 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -8,6 +8,7 @@ from django.views import View import requests from bookwyrm import activitypub, models +from bookwyrm.tasks import app from bookwyrm.signatures import Signature @@ -38,15 +39,30 @@ class Inbox(View): return HttpResponse() return HttpResponse(status=401) - # get the activity dataclass from the type field - try: - activitypub.parse(activity_json) - except (AttributeError, activitypub.ActivitySerializerError): + # just some quick smell tests before we try to parse the json + if not 'object' in activity_json or \ + not 'type' in activity_json or \ + not activity_json['type'] in activitypub.activity_objects: return HttpResponseNotFound() + activity_task.delay() return HttpResponse() +@app.task +def activity_task(activity_json): + ''' do something with this json we think is legit ''' + # lets see if the activitypub module can make sense of this json + try: + activity = activitypub.parse(activity_json) + except activitypub.ActivitySerializerError: + return + + # cool that worked, now we should do the action described by the type + # (create, update, delete, etc) + activity.action() + + def has_valid_signature(request, activity): ''' verify incoming signature ''' try: diff --git a/celerywyrm/celery.py b/celerywyrm/celery.py index 5a53dab5b..2937ef0fc 100644 --- a/celerywyrm/celery.py +++ b/celerywyrm/celery.py @@ -25,5 +25,5 @@ app.autodiscover_tasks( ['bookwyrm'], related_name='connectors.abstract_connector') app.autodiscover_tasks(['bookwyrm'], related_name='emailing') app.autodiscover_tasks(['bookwyrm'], related_name='goodreads_import') -app.autodiscover_tasks(['bookwyrm'], related_name='incoming') app.autodiscover_tasks(['bookwyrm'], related_name='models.user') +app.autodiscover_tasks(['bookwyrm'], related_name='views.inbox')