@@ -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 |
@@ -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 | |||
@@ -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) | |||