commit: 0ec0ad85598ef42ca05d6407868bb5a2fd3ce18f
parent f1d588fd6a04945aa45bd4d1711d142de2e67c43
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date: Sat, 4 Feb 2023 20:51:17 +0000
paginate follow requests (#460)
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
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
Diffstat:
8 files changed, 100 insertions(+), 18 deletions(-)
diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
@@ -147,24 +147,22 @@ 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 get_outgoing_follow_requests(%User{id: id}) do
+ def get_outgoing_follow_requests_query(%User{id: id}) do
__MODULE__
- |> join(:inner, [r], f in assoc(r, :following))
+ |> join(:inner, [r], f in assoc(r, :following), as: :following)
|> where([r], r.state == ^:follow_pending)
|> where([r], r.follower_id == ^id)
- |> where([r, f], f.is_active == true)
- |> select([r, f], f)
- |> Repo.all()
+ |> where([r, following: f], f.is_active == true)
+ |> select([r, following: f], f)
end
def following?(%User{id: follower_id}, %User{id: followed_id}) do
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
@@ -287,8 +287,14 @@ 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_outgoing_follow_requests(user), to: FollowingRelationship
+ defdelegate get_follow_requests_query(user), to: FollowingRelationship
+ defdelegate get_outgoing_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 """
diff --git a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex
@@ -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
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_follow_request_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_follow_request_operation.ex
@@ -17,7 +17,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaFollowRequestOperation do
tags: ["Follow requests"],
summary: "Retrieve outgoing follow requests",
security: [%{"oAuth" => ["read:follows", "follow"]}],
- operationId: "PleromaFollowRequestController.outgoing",
+ operationId: "PleromaFollowRequestController.outgoing",,
+ parameters: pagination_params(),
responses: %{
200 =>
Operation.response("Array of Account", "application/json", %Schema{
@@ -28,4 +29,22 @@ defmodule Pleroma.Web.ApiSpec.PleromaFollowRequestOperation do
}
}
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
diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
@@ -5,6 +5,10 @@
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
use Pleroma.Web, :controller
+ import Pleroma.Web.ControllerHelper,
+ only: [add_link_headers: 2]
+
+ alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
@@ -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"
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -356,7 +356,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
diff --git a/lib/pleroma/web/pleroma_api/controllers/follow_request_controller.ex b/lib/pleroma/web/pleroma_api/controllers/follow_request_controller.ex
@@ -5,6 +5,10 @@
defmodule Pleroma.Web.PleromaAPI.FollowRequestController do
use Pleroma.Web, :controller
+ import Pleroma.Web.ControllerHelper,
+ only: [add_link_headers: 2]
+
+ alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.Plugs.OAuthScopesPlug
@@ -18,10 +22,14 @@ defmodule Pleroma.Web.PleromaAPI.FollowRequestController do
@doc "GET /api/v1/pleroma/outgoing_follow_requests"
def outgoing(%{assigns: %{user: follower}} = conn, _params) do
- follow_requests = User.get_outgoing_follow_requests(follower)
+ follow_requests =
+ follower
+ |> User.get_outgoing_follow_requests_query()
+ |> Pagination.fetch_paginated(params, :keyset, :follower)
conn
|> put_view(Pleroma.Web.MastodonAPI.FollowRequestView)
+ |> add_link_headers(follow_requests)
|> render("index.json", for: follower, users: follow_requests, as: :user)
end
end
diff --git a/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs
@@ -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)