Add support for cancellation of a follow request
authorEgor Kislitsyn <egor@kislitsyn.com>
Tue, 4 Feb 2020 16:35:32 +0000 (20:35 +0400)
committerEgor Kislitsyn <egor@kislitsyn.com>
Wed, 5 Feb 2020 16:22:15 +0000 (20:22 +0400)
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/utils.ex
test/web/activity_pub/activity_pub_test.exs
test/web/common_api/common_api_test.exs
test/web/mastodon_api/controllers/account_controller_test.exs

index 3c86cdb38bbf1bcdf13f684a90cd89db252ec1ab..398c91cf39451cda733e0ada761f1ff96c61a8de 100644 (file)
@@ -647,20 +647,25 @@ defmodule Pleroma.User do
     end
   end
 
+  def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
+    {:error, "Not subscribed!"}
+  end
+
   def unfollow(%User{} = follower, %User{} = followed) do
-    if following?(follower, followed) and follower.ap_id != followed.ap_id do
-      FollowingRelationship.unfollow(follower, followed)
+    case FollowingRelationship.get(follower, followed) do
+      %{state: state} when state in ["accept", "pending"] ->
+        FollowingRelationship.unfollow(follower, followed)
+        {:ok, followed} = update_follower_count(followed)
 
-      {:ok, followed} = update_follower_count(followed)
+        {:ok, follower} =
+          follower
+          |> update_following_count()
+          |> set_cache()
 
-      {:ok, follower} =
-        follower
-        |> update_following_count()
-        |> set_cache()
+        {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
 
-      {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
-    else
-      {:error, "Not subscribed!"}
+      nil ->
+        {:error, "Not subscribed!"}
     end
   end
 
index 4f7fdaf38a1796d00b5bfb6480f9bab5e7107b97..5bca3868bc5dcc3742ced2f474e0cb8e0f79ec82 100644 (file)
@@ -490,6 +490,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     |> Repo.one()
   end
 
+  def fetch_latest_undo(%User{ap_id: ap_id}) do
+    "Undo"
+    |> Activity.Queries.by_type()
+    |> where(actor: ^ap_id)
+    |> order_by([activity], fragment("? desc nulls last", activity.id))
+    |> limit(1)
+    |> Repo.one()
+  end
+
   def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
     %{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
 
index ff4604a524303e676b671372862497378bad4c5d..c8f630266eff9af55683ff16c493d897ac74d5cc 100644 (file)
@@ -1174,6 +1174,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert embedded_object["object"] == followed.ap_id
       assert embedded_object["id"] == follow_activity.data["id"]
     end
+
+    test "creates an undo activity for a pending follow request" do
+      follower = insert(:user)
+      followed = insert(:user, %{locked: true})
+
+      {:ok, follow_activity} = ActivityPub.follow(follower, followed)
+      {:ok, activity} = ActivityPub.unfollow(follower, followed)
+
+      assert activity.data["type"] == "Undo"
+      assert activity.data["actor"] == follower.ap_id
+
+      embedded_object = activity.data["object"]
+      assert is_map(embedded_object)
+      assert embedded_object["type"] == "Follow"
+      assert embedded_object["object"] == followed.ap_id
+      assert embedded_object["id"] == follow_activity.data["id"]
+    end
   end
 
   describe "blocking / unblocking" do
index 8fa0c6faa952d7c7a6bb6fbe38c659df8c4d9190..2bbe6c923c629adf825e8fc7050bef8e5a4c5071 100644 (file)
@@ -536,6 +536,30 @@ defmodule Pleroma.Web.CommonAPITest do
 
       refute User.subscribed_to?(follower, followed)
     end
+
+    test "cancels a pending follow" do
+      follower = insert(:user)
+      followed = insert(:user, locked: true)
+
+      assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
+               CommonAPI.follow(follower, followed)
+
+      assert %{state: "pending"} = Pleroma.FollowingRelationship.get(follower, followed)
+
+      assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
+
+      assert Pleroma.FollowingRelationship.get(follower, followed) == nil
+
+      assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
+               Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
+
+      assert %{
+               data: %{
+                 "type" => "Undo",
+                 "object" => %{"type" => "Follow", "state" => "cancelled"}
+               }
+             } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
+    end
   end
 
   describe "accept_follow_request/2" do
index ec1e180025c03afc0aec36c782386cb2aa2b37a4..e2abcd7c5b36e84fab84054d851e6c01b8b9a6b7 100644 (file)
@@ -457,6 +457,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
       assert id == to_string(other_user.id)
     end
 
+    test "cancelling follow request", %{conn: conn} do
+      %{id: other_user_id} = insert(:user, %{locked: true})
+
+      assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
+               conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
+
+      assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
+               conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
+    end
+
     test "following without reblogs" do
       %{conn: conn} = oauth_access(["follow", "read:statuses"])
       followed = insert(:user)