Merge pull request #236 from mouse-reeve/relationship-activitypub

Uses activitypub mixin in relationship models
This commit is contained in:
Mouse Reeve 2020-10-16 11:04:14 -07:00 committed by GitHub
commit 43c6b9880a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 22 deletions

View file

@ -129,7 +129,7 @@ def handle_follow(activity):
relationship = models.UserFollowRequest.objects.create( relationship = models.UserFollowRequest.objects.create(
user_subject=actor, user_subject=actor,
user_object=to_follow, user_object=to_follow,
relationship_id=activity['id'] remote_id=activity['id']
) )
except django.db.utils.IntegrityError as err: except django.db.utils.IntegrityError as err:
if err.__cause__.diag.constraint_name != 'userfollowrequest_unique': if err.__cause__.diag.constraint_name != 'userfollowrequest_unique':

View file

@ -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',
),
]

View file

@ -2,10 +2,10 @@
from django.db import models from django.db import models
from bookwyrm import activitypub 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 ''' ''' many-to-many through table for followers '''
user_subject = models.ForeignKey( user_subject = models.ForeignKey(
'User', 'User',
@ -17,8 +17,6 @@ class UserRelationship(BookWyrmModel):
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='%(class)s_user_object' related_name='%(class)s_user_object'
) )
# follow or follow_request for pending TODO: blocking?
relationship_id = models.CharField(max_length=100)
class Meta: class Meta:
''' relationships should be unique ''' ''' 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 ''' ''' use shelf identifier in remote_id '''
status = status or 'follows'
base_path = self.user_subject.remote_id 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): def to_accept_activity(self):
''' generate an Accept for this follow request ''' ''' generate an Accept for this follow request '''
return activitypub.Accept( return activitypub.Accept(
id='%s#accepts/follows/' % self.remote_id, id=self.get_remote_id(status='accepts'),
actor=self.user_subject.remote_id, actor=self.user_object.remote_id,
object=self.user_object.remote_id, object=self.to_activity()
).serialize() ).serialize()
def to_reject_activity(self): def to_reject_activity(self):
''' generate an Accept for this follow request ''' ''' generate an Accept for this follow request '''
return activitypub.Reject( return activitypub.Reject(
id='%s#rejects/follows/' % self.remote_id, id=self.get_remote_id(status='rejects'),
actor=self.user_subject.remote_id, actor=self.user_object.remote_id,
object=self.user_object.remote_id, object=self.to_activity()
).serialize() ).serialize()
@ -66,7 +74,7 @@ class UserFollows(UserRelationship):
return cls( return cls(
user_subject=follow_request.user_subject, user_subject=follow_request.user_subject,
user_object=follow_request.user_object, 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 ''' ''' following a user requires manual or automatic confirmation '''
status = 'follow_request' 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): class UserBlocks(UserRelationship):
''' prevent another user from following you and seeing your posts ''' ''' prevent another user from following you and seeing your posts '''

View file

@ -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)