From 2a0af0138dcdf1e9631c37f7a822fa005548e12b Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 16 Oct 2020 10:37:33 -0700 Subject: [PATCH] Uses activitypub mixin in relationship models plus tests --- bookwyrm/incoming.py | 2 +- .../migrations/0054_auto_20201016_1707.py | 25 ++++ bookwyrm/models/relationship.py | 42 +++--- .../tests/models/test_relationship_models.py | 120 ++++++++++++++++++ 4 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 bookwyrm/migrations/0054_auto_20201016_1707.py create mode 100644 bookwyrm/tests/models/test_relationship_models.py diff --git a/bookwyrm/incoming.py b/bookwyrm/incoming.py index b223ab16..c9e88dbb 100644 --- a/bookwyrm/incoming.py +++ b/bookwyrm/incoming.py @@ -129,7 +129,7 @@ def handle_follow(activity): relationship = models.UserFollowRequest.objects.create( user_subject=user, user_object=to_follow, - relationship_id=activity['id'] + remote_id=activity['id'] ) except django.db.utils.IntegrityError as err: if err.__cause__.diag.constraint_name != 'userfollowrequest_unique': diff --git a/bookwyrm/migrations/0054_auto_20201016_1707.py b/bookwyrm/migrations/0054_auto_20201016_1707.py new file mode 100644 index 00000000..043ff12d --- /dev/null +++ b/bookwyrm/migrations/0054_auto_20201016_1707.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.7 on 2020-10-16 17:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0053_auto_20201014_1700'), + ] + + operations = [ + migrations.RemoveField( + model_name='userblocks', + name='relationship_id', + ), + migrations.RemoveField( + model_name='userfollowrequest', + name='relationship_id', + ), + migrations.RemoveField( + model_name='userfollows', + name='relationship_id', + ), + ] diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py index e357955e..e86b9098 100644 --- a/bookwyrm/models/relationship.py +++ b/bookwyrm/models/relationship.py @@ -2,10 +2,10 @@ from django.db import models from bookwyrm import activitypub -from .base_model import BookWyrmModel +from .base_model import ActivitypubMixin, ActivityMapping, BookWyrmModel -class UserRelationship(BookWyrmModel): +class UserRelationship(ActivitypubMixin, BookWyrmModel): ''' many-to-many through table for followers ''' user_subject = models.ForeignKey( 'User', @@ -17,8 +17,6 @@ class UserRelationship(BookWyrmModel): on_delete=models.PROTECT, related_name='%(class)s_user_object' ) - # follow or follow_request for pending TODO: blocking? - relationship_id = models.CharField(max_length=100) class Meta: ''' relationships should be unique ''' @@ -34,25 +32,35 @@ class UserRelationship(BookWyrmModel): ) ] - def get_remote_id(self): + activity_mappings = [ + ActivityMapping('id', 'remote_id'), + ActivityMapping('actor', 'user_subject'), + ActivityMapping('object', 'user_object'), + ] + activity_serializer = activitypub.Follow + + def get_remote_id(self, status=None): ''' use shelf identifier in remote_id ''' + status = status or 'follows' base_path = self.user_subject.remote_id - return '%s#%s/%d' % (base_path, self.status, self.id) + return '%s#%s/%d' % (base_path, status, self.id) + def to_accept_activity(self): ''' generate an Accept for this follow request ''' return activitypub.Accept( - id='%s#accepts/follows/' % self.remote_id, - actor=self.user_subject.remote_id, - object=self.user_object.remote_id, + id=self.get_remote_id(status='accepts'), + actor=self.user_object.remote_id, + object=self.to_activity() ).serialize() + def to_reject_activity(self): ''' generate an Accept for this follow request ''' return activitypub.Reject( - id='%s#rejects/follows/' % self.remote_id, - actor=self.user_subject.remote_id, - object=self.user_object.remote_id, + id=self.get_remote_id(status='rejects'), + actor=self.user_object.remote_id, + object=self.to_activity() ).serialize() @@ -66,7 +74,7 @@ class UserFollows(UserRelationship): return cls( user_subject=follow_request.user_subject, user_object=follow_request.user_object, - relationship_id=follow_request.relationship_id, + remote_id=follow_request.remote_id, ) @@ -74,14 +82,6 @@ class UserFollowRequest(UserRelationship): ''' following a user requires manual or automatic confirmation ''' status = 'follow_request' - def to_activity(self): - ''' request activity ''' - return activitypub.Follow( - id=self.remote_id, - actor=self.user_subject.remote_id, - object=self.user_object.remote_id, - ).serialize() - class UserBlocks(UserRelationship): ''' prevent another user from following you and seeing your posts ''' diff --git a/bookwyrm/tests/models/test_relationship_models.py b/bookwyrm/tests/models/test_relationship_models.py new file mode 100644 index 00000000..1e763f59 --- /dev/null +++ b/bookwyrm/tests/models/test_relationship_models.py @@ -0,0 +1,120 @@ +''' testing models ''' +from django.test import TestCase + +from bookwyrm import models + + +class Relationship(TestCase): + def setUp(self): + self.remote_user = models.User.objects.create_user( + 'rat', 'rat@rat.com', 'ratword', + local=False, + remote_id='https://example.com/users/rat', + inbox='https://example.com/users/rat/inbox', + outbox='https://example.com/users/rat/outbox', + ) + self.local_user = models.User.objects.create_user( + 'mouse', 'mouse@mouse.com', 'mouseword') + self.local_user.remote_id = 'http://local.com/user/mouse' + self.local_user.save() + + def test_user_follows(self): + rel = models.UserFollows.objects.create( + user_subject=self.local_user, + user_object=self.remote_user + ) + + self.assertEqual( + rel.remote_id, + 'http://local.com/user/mouse#follows/%d' % rel.id + ) + + activity = rel.to_activity() + self.assertEqual(activity['id'], rel.remote_id) + self.assertEqual(activity['actor'], self.local_user.remote_id) + self.assertEqual(activity['object'], self.remote_user.remote_id) + + def test_user_follow_accept_serialization(self): + rel = models.UserFollows.objects.create( + user_subject=self.local_user, + user_object=self.remote_user + ) + + self.assertEqual( + rel.remote_id, + 'http://local.com/user/mouse#follows/%d' % rel.id + ) + accept = rel.to_accept_activity() + self.assertEqual(accept['type'], 'Accept') + self.assertEqual( + accept['id'], + 'http://local.com/user/mouse#accepts/%d' % rel.id + ) + self.assertEqual(accept['actor'], self.remote_user.remote_id) + self.assertEqual(accept['object']['id'], rel.remote_id) + self.assertEqual(accept['object']['actor'], self.local_user.remote_id) + self.assertEqual(accept['object']['object'], self.remote_user.remote_id) + + def test_user_follow_reject_serialization(self): + rel = models.UserFollows.objects.create( + user_subject=self.local_user, + user_object=self.remote_user + ) + + self.assertEqual( + rel.remote_id, + 'http://local.com/user/mouse#follows/%d' % rel.id + ) + reject = rel.to_reject_activity() + self.assertEqual(reject['type'], 'Reject') + self.assertEqual( + reject['id'], + 'http://local.com/user/mouse#rejects/%d' % rel.id + ) + self.assertEqual(reject['actor'], self.remote_user.remote_id) + self.assertEqual(reject['object']['id'], rel.remote_id) + self.assertEqual(reject['object']['actor'], self.local_user.remote_id) + self.assertEqual(reject['object']['object'], self.remote_user.remote_id) + + + def test_user_follows_from_request(self): + request = models.UserFollowRequest.objects.create( + user_subject=self.local_user, + user_object=self.remote_user + ) + self.assertEqual( + request.remote_id, + 'http://local.com/user/mouse#follows/%d' % request.id + ) + self.assertEqual(request.status, 'follow_request') + + rel = models.UserFollows.from_request(request) + self.assertEqual( + rel.remote_id, + 'http://local.com/user/mouse#follows/%d' % request.id + ) + self.assertEqual(rel.status, 'follows') + self.assertEqual(rel.user_subject, self.local_user) + self.assertEqual(rel.user_object, self.remote_user) + + + def test_user_follows_from_request_custom_remote_id(self): + request = models.UserFollowRequest.objects.create( + user_subject=self.local_user, + user_object=self.remote_user, + remote_id='http://antoher.server/sdkfhskdjf/23' + ) + self.assertEqual( + request.remote_id, + 'http://antoher.server/sdkfhskdjf/23' + ) + self.assertEqual(request.status, 'follow_request') + + rel = models.UserFollows.from_request(request) + self.assertEqual( + rel.remote_id, + 'http://antoher.server/sdkfhskdjf/23' + ) + self.assertEqual(rel.status, 'follows') + self.assertEqual(rel.user_subject, self.local_user) + self.assertEqual(rel.user_object, self.remote_user)