From 984b5f8adfd1e7b981a270193d1ebd76f468576c Mon Sep 17 00:00:00 2001 From: eugenijm Date: Mon, 11 Mar 2019 21:03:30 +0300 Subject: [PATCH] MastoAPI followers/following endpoints --- lib/pleroma/web/mastodon_api/mastodon_api.ex | 62 ++++++++++++++ .../mastodon_api/mastodon_api_controller.ex | 11 ++- .../mastodon_api_controller_test.exs | 82 +++++++++++++++++++ 3 files changed, 151 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 8b1378917..c952c5806 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -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 diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 26921d386..bfc8dcd0a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -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 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) 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 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) 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 @@ def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do end conn + |> add_link_headers(:following, followers, user) |> put_view(AccountView) |> render("accounts.json", %{users: followers, as: :user}) end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 8a20eef2c..d9e41d356 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1184,6 +1184,47 @@ test "getting followers, hide_followers, same user requesting", %{conn: conn} 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 @@ test "getting following, hide_follows, same user requesting", %{conn: conn} 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)