Merge pull request #979 from bookwyrm-social/allow-refollow

Allow refollow
This commit is contained in:
Mouse Reeve 2021-04-22 08:56:48 -07:00 committed by GitHub
commit 9c87c91c62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 10 deletions

View file

@ -101,12 +101,15 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
def save(self, *args, broadcast=True, **kwargs): def save(self, *args, broadcast=True, **kwargs):
""" make sure the follow or block relationship doesn't already exist """ """ make sure the follow or block relationship doesn't already exist """
# don't create a request if a follow already exists # if there's a request for a follow that already exists, accept it
# without changing the local database state
if UserFollows.objects.filter( if UserFollows.objects.filter(
user_subject=self.user_subject, user_subject=self.user_subject,
user_object=self.user_object, user_object=self.user_object,
).exists(): ).exists():
raise IntegrityError() self.accept(broadcast_only=True)
return
# blocking in either direction is a no-go # blocking in either direction is a no-go
if UserBlocks.objects.filter( if UserBlocks.objects.filter(
Q( Q(
@ -141,9 +144,9 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
""" get id for sending an accept or reject of a local user """ """ get id for sending an accept or reject of a local user """
base_path = self.user_object.remote_id base_path = self.user_object.remote_id
return "%s#%s/%d" % (base_path, status, self.id) return "%s#%s/%d" % (base_path, status, self.id or 0)
def accept(self): def accept(self, broadcast_only=False):
""" turn this request into the real deal""" """ turn this request into the real deal"""
user = self.user_object user = self.user_object
if not self.user_subject.local: if not self.user_subject.local:
@ -153,6 +156,9 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
object=self.to_activity(), object=self.to_activity(),
).serialize() ).serialize()
self.broadcast(activity, user) self.broadcast(activity, user)
if broadcast_only:
return
with transaction.atomic(): with transaction.atomic():
UserFollows.from_request(self) UserFollows.from_request(self)
self.delete() self.delete()

View file

@ -1,4 +1,5 @@
""" tests incoming activities""" """ tests incoming activities"""
import json
from unittest.mock import patch from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
@ -34,7 +35,7 @@ class InboxRelationships(TestCase):
models.SiteSettings.objects.create() models.SiteSettings.objects.create()
def test_handle_follow(self): def test_follow(self):
""" remote user wants to follow local user """ """ remote user wants to follow local user """
activity = { activity = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
@ -48,6 +49,8 @@ class InboxRelationships(TestCase):
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock: with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
views.inbox.activity_task(activity) views.inbox.activity_task(activity)
self.assertEqual(mock.call_count, 1) self.assertEqual(mock.call_count, 1)
response_activity = json.loads(mock.call_args[0][1])
self.assertEqual(response_activity["type"], "Accept")
# notification created # notification created
notification = models.Notification.objects.get() notification = models.Notification.objects.get()
@ -61,7 +64,34 @@ class InboxRelationships(TestCase):
follow = models.UserFollows.objects.get(user_object=self.local_user) follow = models.UserFollows.objects.get(user_object=self.local_user)
self.assertEqual(follow.user_subject, self.remote_user) self.assertEqual(follow.user_subject, self.remote_user)
def test_handle_follow_manually_approved(self): def test_follow_duplicate(self):
""" remote user wants to follow local user twice """
activity = {
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.com/users/rat/follows/123",
"type": "Follow",
"actor": "https://example.com/users/rat",
"object": "https://example.com/user/mouse",
}
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
views.inbox.activity_task(activity)
# the follow relationship should exist
follow = models.UserFollows.objects.get(user_object=self.local_user)
self.assertEqual(follow.user_subject, self.remote_user)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
views.inbox.activity_task(activity)
self.assertEqual(mock.call_count, 1)
response_activity = json.loads(mock.call_args[0][1])
self.assertEqual(response_activity["type"], "Accept")
# the follow relationship should STILL exist
follow = models.UserFollows.objects.get(user_object=self.local_user)
self.assertEqual(follow.user_subject, self.remote_user)
def test_follow_manually_approved(self):
""" needs approval before following """ """ needs approval before following """
activity = { activity = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
@ -91,7 +121,7 @@ class InboxRelationships(TestCase):
follow = models.UserFollows.objects.all() follow = models.UserFollows.objects.all()
self.assertEqual(list(follow), []) self.assertEqual(list(follow), [])
def test_handle_undo_follow_request(self): def test_undo_follow_request(self):
""" the requester cancels a follow request """ """ the requester cancels a follow request """
self.local_user.manually_approves_followers = True self.local_user.manually_approves_followers = True
self.local_user.save(broadcast=False) self.local_user.save(broadcast=False)
@ -121,7 +151,7 @@ class InboxRelationships(TestCase):
self.assertFalse(self.local_user.follower_requests.exists()) self.assertFalse(self.local_user.follower_requests.exists())
def test_handle_unfollow(self): def test_unfollow(self):
""" remove a relationship """ """ remove a relationship """
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
rel = models.UserFollows.objects.create( rel = models.UserFollows.objects.create(
@ -146,7 +176,7 @@ class InboxRelationships(TestCase):
views.inbox.activity_task(activity) views.inbox.activity_task(activity)
self.assertIsNone(self.local_user.followers.first()) self.assertIsNone(self.local_user.followers.first())
def test_handle_follow_accept(self): def test_follow_accept(self):
""" a remote user approved a follow request from local """ """ a remote user approved a follow request from local """
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
rel = models.UserFollowRequest.objects.create( rel = models.UserFollowRequest.objects.create(
@ -177,7 +207,7 @@ class InboxRelationships(TestCase):
self.assertEqual(follows.count(), 1) self.assertEqual(follows.count(), 1)
self.assertEqual(follows.first(), self.local_user) self.assertEqual(follows.first(), self.local_user)
def test_handle_follow_reject(self): def test_follow_reject(self):
""" turn down a follow request """ """ turn down a follow request """
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
rel = models.UserFollowRequest.objects.create( rel = models.UserFollowRequest.objects.create(