paginate follow requests (#460)
authorfloatingghost <hannah@coffee-and-dreams.uk>
Sat, 4 Feb 2023 20:51:17 +0000 (20:51 +0000)
committerfloatingghost <hannah@coffee-and-dreams.uk>
Sat, 4 Feb 2023 20:51:17 +0000 (20:51 +0000)
matches https://docs.joinmastodon.org/methods/follow_requests/#get mostly

Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/460

CHANGELOG.md
lib/pleroma/following_relationship.ex
lib/pleroma/user.ex
lib/pleroma/web/api_spec/operations/follow_request_operation.ex
lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
test/pleroma/web/activity_pub/object_validators/block_validation_test.exs
test/pleroma/web/common_api_test.exs
test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs

index 0437033eefd21907b93c2acd8af1e3b068915213..e6effac7820395955e6728250eb40df718fa9662 100644 (file)
@@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Simplified HTTP signature processing
 - Rich media will now hard-exit after 5 seconds, to prevent timeline hangs
 - HTTP Content Security Policy is now far more strict to prevent any potential XSS/CSS leakages
+- Follow requests are now paginated, matches mastodon API spec, so use the Link header to paginate.
 
 ### Fixed 
 - /api/v1/accounts/lookup will now respect restrict\_unauthenticated
index c489ccbbe0d44cea41b4806ba4313259011179f3..9e75458e52c411c2ba8b23f8fa6ec82b605c54e9 100644 (file)
@@ -155,14 +155,13 @@ defmodule Pleroma.FollowingRelationship do
     |> Repo.aggregate(:count, :id)
   end
 
-  def get_follow_requests(%User{id: id}) do
+  def get_follow_requests_query(%User{id: id}) do
     __MODULE__
-    |> join(:inner, [r], f in assoc(r, :follower))
+    |> join(:inner, [r], f in assoc(r, :follower), as: :follower)
     |> where([r], r.state == ^:follow_pending)
     |> where([r], r.following_id == ^id)
-    |> where([r, f], f.is_active == true)
-    |> select([r, f], f)
-    |> Repo.all()
+    |> where([r, follower: f], f.is_active == true)
+    |> select([r, follower: f], f)
   end
 
   def following?(%User{id: follower_id}, %User{id: followed_id}) do
index 1ddbd36a828fbbe935272e700e213ea68100fa28..1572a895ec0bb308e4c8a81ff0251b32e948921a 100644 (file)
@@ -273,7 +273,13 @@ defmodule Pleroma.User do
   defdelegate following(user), to: FollowingRelationship
   defdelegate following?(follower, followed), to: FollowingRelationship
   defdelegate following_ap_ids(user), to: FollowingRelationship
-  defdelegate get_follow_requests(user), to: FollowingRelationship
+  defdelegate get_follow_requests_query(user), to: FollowingRelationship
+
+  def get_follow_requests(user) do
+    get_follow_requests_query(user)
+    |> Repo.all()
+  end
+
   defdelegate search(query, opts \\ []), to: User.Search
 
   @doc """
index 7840196999513dea1498560380f02b45bd06587b..d6f59191b61074612ad2280217f0c9f6520b2fac 100644 (file)
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do
       summary: "Retrieve follow requests",
       security: [%{"oAuth" => ["read:follows", "follow"]}],
       operationId: "FollowRequestController.index",
+      parameters: pagination_params(),
       responses: %{
         200 =>
           Operation.response("Array of Account", "application/json", %Schema{
@@ -62,4 +63,22 @@ defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do
       required: true
     )
   end
+
+  defp pagination_params do
+    [
+      Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
+      Operation.parameter(
+        :since_id,
+        :query,
+        :string,
+        "Return the oldest items newer than this ID"
+      ),
+      Operation.parameter(
+        :limit,
+        :query,
+        %Schema{type: :integer, default: 20},
+        "Maximum number of items to return. Will be ignored if it's more than 40"
+      )
+    ]
+  end
 end
index d915298f11824c9ed51c7d4e660c87c5e61540a4..e534d0388c561cb2fa4558fa1d95d69262404d9f 100644 (file)
@@ -5,9 +5,13 @@
 defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
   use Pleroma.Web, :controller
 
+  import Pleroma.Web.ControllerHelper,
+    only: [add_link_headers: 2]
+
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.Plugs.OAuthScopesPlug
+  alias Pleroma.Pagination
 
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
   plug(:assign_follower when action != :index)
@@ -24,10 +28,15 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
   defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FollowRequestOperation
 
   @doc "GET /api/v1/follow_requests"
-  def index(%{assigns: %{user: followed}} = conn, _params) do
-    follow_requests = User.get_follow_requests(followed)
+  def index(%{assigns: %{user: followed}} = conn, params) do
+    follow_requests =
+      followed
+      |> User.get_follow_requests_query()
+      |> Pagination.fetch_paginated(params, :keyset, :follower)
 
-    render(conn, "index.json", for: followed, users: follow_requests, as: :user)
+    conn
+    |> add_link_headers(follow_requests)
+    |> render("index.json", for: followed, users: follow_requests, as: :user)
   end
 
   @doc "POST /api/v1/follow_requests/:id/authorize"
index 653a50e20aecc3a4b76eb70a16a56d8814399ccd..190d6ebf235bf43d77af55c1ecd1f08195f86552 100644 (file)
@@ -334,7 +334,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
          %User{id: user_id}
        ) do
     count =
-      User.get_follow_requests(user)
+      user
+      |> User.get_follow_requests()
       |> length()
 
     data
index ad619089238860337c09416e1c99cdbfc48b7e57..5e675776090e74746b196582e4248ec69635441d 100644 (file)
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidationTest do
 
   describe "blocks" do
     setup do
+      clear_config([:activitypub, :outgoing_blocks], true)
       user = insert(:user, local: false)
       blocked = insert(:user)
 
index 2b7a34be23f6ef12ef994275a39de6cbdd56276c..33709c8f3bf67a5dd2fc9e6a5ab93dbf1a6e9fdc 100644 (file)
@@ -71,6 +71,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
     test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
       clear_config([:instance, :federating], true)
+      clear_config([:activitypub, :outgoing_blocks], true)
 
       with_mock Pleroma.Web.Federator,
         publish: fn _ -> nil end do
index 069ffb3d6d4803ed11616706b2d5c5f7b89be6a2..e3f59d88621ddac5c8709b9c02ac8251b7024e09 100644 (file)
@@ -10,6 +10,11 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
 
   import Pleroma.Factory
 
+  defp extract_next_link_header(header) do
+    [_, next_link] = Regex.run(~r{<(?<next_link>.*)>; rel="next"}, header)
+    next_link
+  end
+
   describe "locked accounts" do
     setup do
       user = insert(:user, is_locked: true)
@@ -31,6 +36,23 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
       assert to_string(other_user.id) == relationship["id"]
     end
 
+    test "/api/v1/follow_requests paginates", %{user: user, conn: conn} do
+      for _ <- 1..21 do
+        other_user = insert(:user)
+        {:ok, _, _, _activity} = CommonAPI.follow(other_user, user)
+        {:ok, _, _} = User.follow(other_user, user, :follow_pending)
+      end
+
+      conn = get(conn, "/api/v1/follow_requests")
+      assert length(json_response_and_validate_schema(conn, 200)) == 20
+      assert [link_header] = get_resp_header(conn, "link")
+      assert link_header =~ "rel=\"next\""
+      next_link = extract_next_link_header(link_header)
+      assert next_link =~ "/api/v1/follow_requests"
+      conn = get(conn, next_link)
+      assert length(json_response_and_validate_schema(conn, 200)) == 1
+    end
+
     test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do
       other_user = insert(:user)