@@ -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.MastodonView | ||||
alias Pleroma.Web.MastodonAPI.StatusView | alias Pleroma.Web.MastodonAPI.StatusView | ||||
alias Pleroma.Web.MastodonAPI.ReportView | alias Pleroma.Web.MastodonAPI.ReportView | ||||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||||
alias Pleroma.Web.ActivityPub.ActivityPub | alias Pleroma.Web.ActivityPub.ActivityPub | ||||
alias Pleroma.Web.ActivityPub.Utils | alias Pleroma.Web.ActivityPub.Utils | ||||
alias Pleroma.Web.ActivityPub.Visibility | alias Pleroma.Web.ActivityPub.Visibility | ||||
@@ -652,9 +653,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||||
|> render("index.json", %{activities: activities, for: user, as: :activity}) | |> render("index.json", %{activities: activities, for: user, as: :activity}) | ||||
end | 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), | with %User{} = user <- Repo.get(User, id), | ||||
{:ok, followers} <- User.get_followers(user) do | |||||
followers <- MastodonAPI.get_followers(user, params) do | |||||
followers = | followers = | ||||
cond do | cond do | ||||
for_user && user.id == for_user.id -> followers | for_user && user.id == for_user.id -> followers | ||||
@@ -663,14 +664,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||||
end | end | ||||
conn | conn | ||||
|> add_link_headers(:followers, followers, user) | |||||
|> put_view(AccountView) | |> put_view(AccountView) | ||||
|> render("accounts.json", %{users: followers, as: :user}) | |> render("accounts.json", %{users: followers, as: :user}) | ||||
end | end | ||||
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), | with %User{} = user <- Repo.get(User, id), | ||||
{:ok, followers} <- User.get_friends(user) do | |||||
followers <- MastodonAPI.get_friends(user, params) do | |||||
followers = | followers = | ||||
cond do | cond do | ||||
for_user && user.id == for_user.id -> followers | for_user && user.id == for_user.id -> followers | ||||
@@ -679,6 +681,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||||
end | end | ||||
conn | conn | ||||
|> add_link_headers(:following, followers, user) | |||||
|> put_view(AccountView) | |> put_view(AccountView) | ||||
|> render("accounts.json", %{users: followers, as: :user}) | |> render("accounts.json", %{users: followers, as: :user}) | ||||
end | end | ||||
@@ -1184,6 +1184,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||||
refute [] == json_response(conn, 200) | refute [] == json_response(conn, 200) | ||||
end | 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 | test "getting following", %{conn: conn} do | ||||
user = insert(:user) | user = insert(:user) | ||||
other_user = insert(:user) | other_user = insert(:user) | ||||
@@ -1222,6 +1263,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||||
refute [] == json_response(conn, 200) | refute [] == json_response(conn, 200) | ||||
end | 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 | test "following / unfollowing a user", %{conn: conn} do | ||||
user = insert(:user) | user = insert(:user) | ||||
other_user = insert(:user) | other_user = insert(:user) | ||||