MastoAPI followers/following endpoints
authoreugenijm <eugenijm@protonmail.com>
Mon, 11 Mar 2019 18:03:30 +0000 (21:03 +0300)
committereugenijm <eugenijm@protonmail.com>
Tue, 12 Mar 2019 13:01:24 +0000 (16:01 +0300)
lib/pleroma/web/mastodon_api/mastodon_api.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
test/web/mastodon_api/mastodon_api_controller_test.exs

index 8b137891791fe96927ad78e64b0aad7bded08bdc..c952c5806ebfed326dbcc9dacd63154705935b16 100644 (file)
@@ -1 +1,63 @@
+defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
+  import Ecto.Query
+  import Ecto.Changeset
 
+  alias Pleroma.User
+  alias Pleroma.Repo
+
+  @default_limit 20
+
+  def get_followers(user, params \\ %{}) do
+    user
+    |> User.get_followers_query()
+    |> paginate(params)
+    |> Repo.all()
+  end
+
+  def get_friends(user, params \\ %{}) do
+    user
+    |> User.get_friends_query()
+    |> paginate(params)
+    |> Repo.all()
+  end
+
+  def paginate(query, params \\ %{}) do
+    options = cast_params(params)
+
+    query
+    |> restrict(:max_id, options)
+    |> restrict(:since_id, options)
+    |> restrict(:limit, options)
+    |> order_by([u], fragment("? desc nulls last", u.id))
+  end
+
+  def cast_params(params) do
+    param_types = %{
+      max_id: :string,
+      since_id: :string,
+      limit: :integer
+    }
+
+    changeset = cast({%{}, param_types}, params, Map.keys(param_types))
+    changeset.changes
+  end
+
+  defp restrict(query, :max_id, %{max_id: max_id}) do
+    query
+    |> where([q], q.id < ^max_id)
+  end
+
+  defp restrict(query, :since_id, %{since_id: since_id}) do
+    query
+    |> where([q], q.id > ^since_id)
+  end
+
+  defp restrict(query, :limit, options) do
+    limit = Map.get(options, :limit, @default_limit)
+
+    query
+    |> limit(^limit)
+  end
+
+  defp restrict(query, _, _), do: query
+end
index 26921d3862b481f66649271240026a492b15c6c8..bfc8dcd0a3d1f9f562a18924605242d075cae2a1 100644 (file)
@@ -22,6 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   alias Pleroma.Web.MastodonAPI.MastodonView
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.MastodonAPI.ReportView
+  alias Pleroma.Web.MastodonAPI.MastodonAPI
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
@@ -652,9 +653,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     |> render("index.json", %{activities: activities, for: user, as: :activity})
   end
 
-  def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
+  def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
     with %User{} = user <- Repo.get(User, id),
-         {:ok, followers} <- User.get_followers(user) do
+         followers <- MastodonAPI.get_followers(user, params) do
       followers =
         cond do
           for_user && user.id == for_user.id -> followers
@@ -663,14 +664,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         end
 
       conn
+      |> add_link_headers(:followers, followers, user)
       |> put_view(AccountView)
       |> render("accounts.json", %{users: followers, as: :user})
     end
   end
 
-  def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
+  def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
     with %User{} = user <- Repo.get(User, id),
-         {:ok, followers} <- User.get_friends(user) do
+         followers <- MastodonAPI.get_friends(user, params) do
       followers =
         cond do
           for_user && user.id == for_user.id -> followers
@@ -679,6 +681,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         end
 
       conn
+      |> add_link_headers(:following, followers, user)
       |> put_view(AccountView)
       |> render("accounts.json", %{users: followers, as: :user})
     end
index 8a20eef2cc93cb862f9603fc016979b4f7c70e90..d9e41d356501bd1f4bf00810fb47570f2a26598c 100644 (file)
@@ -1184,6 +1184,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     refute [] == json_response(conn, 200)
   end
 
+  test "getting followers, pagination", %{conn: conn} do
+    user = insert(:user)
+    follower1 = insert(:user)
+    follower2 = insert(:user)
+    follower3 = insert(:user)
+    {:ok, _} = User.follow(follower1, user)
+    {:ok, _} = User.follow(follower2, user)
+    {:ok, _} = User.follow(follower3, user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
+
+    assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
+    assert id3 == follower3.id
+    assert id2 == follower2.id
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
+
+    assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
+    assert id2 == follower2.id
+    assert id1 == follower1.id
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
+
+    assert [%{"id" => id2}] = json_response(res_conn, 200)
+    assert id2 == follower2.id
+
+    assert [link_header] = get_resp_header(res_conn, "link")
+    assert link_header =~ ~r/since_id=#{follower2.id}/
+    assert link_header =~ ~r/max_id=#{follower2.id}/
+  end
+
   test "getting following", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
@@ -1222,6 +1263,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     refute [] == json_response(conn, 200)
   end
 
+  test "getting following, pagination", %{conn: conn} do
+    user = insert(:user)
+    following1 = insert(:user)
+    following2 = insert(:user)
+    following3 = insert(:user)
+    {:ok, _} = User.follow(user, following1)
+    {:ok, _} = User.follow(user, following2)
+    {:ok, _} = User.follow(user, following3)
+
+    conn =
+      conn
+      |> assign(:user, user)
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
+
+    assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
+    assert id3 == following3.id
+    assert id2 == following2.id
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
+
+    assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
+    assert id2 == following2.id
+    assert id1 == following1.id
+
+    res_conn =
+      conn
+      |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
+
+    assert [%{"id" => id2}] = json_response(res_conn, 200)
+    assert id2 == following2.id
+
+    assert [link_header] = get_resp_header(res_conn, "link")
+    assert link_header =~ ~r/since_id=#{following2.id}/
+    assert link_header =~ ~r/max_id=#{following2.id}/
+  end
+
   test "following / unfollowing a user", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)