Merge branch 'endorsements-api' into 'develop'

Support new Mastodon API for endorsed accounts

See merge request pleroma/pleroma!4361
This commit is contained in:
nicole mikołajczyk 2025-11-28 14:51:06 +01:00
commit e81e0d64c1
8 changed files with 84 additions and 84 deletions

View file

@ -0,0 +1 @@
Support new Mastodon API for endorsed accounts

View file

@ -398,6 +398,28 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
def endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
description: "Returns endorsed accounts",
operationId: "AccountController.endorsements",
parameters: [
with_relationships_param(),
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def remove_from_followers_operation do
%Operation{
tags: ["Account actions"],
@ -500,11 +522,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
def endorsements_operation do
def own_endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
operationId: "AccountController.endorsements",
operationId: "AccountController.own_endorsements",
description: "Returns endorsed accounts",
security: [%{"oAuth" => ["read:accounts"]}],
responses: %{

View file

@ -64,25 +64,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
}
end
def endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
description: "Returns endorsed accounts",
operationId: "PleromaAPI.AccountController.endorsements",
parameters: [with_relationships_param(), id_param()],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
AccountOperation.array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def subscribe_operation do
%Operation{
deprecated: true,

View file

@ -38,7 +38,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
when action in [:show, :followers, :following]
when action in [:show, :followers, :following, :endorsements]
)
plug(
@ -50,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{scopes: ["read:accounts"]}
when action in [:verify_credentials, :endorsements]
when action in [:verify_credentials, :endorsements, :own_endorsements]
)
plug(
@ -89,7 +89,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@relationship_actions [:follow, :unfollow, :remove_from_followers]
@needs_account ~W(
followers following lists follow unfollow mute unmute block unblock
note endorse unendorse remove_from_followers
note endorse unendorse endorsements remove_from_followers
)a
plug(
@ -555,6 +555,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
@doc "GET /api/v1/accounts/:id/endorsements"
def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
|> Pleroma.Repo.all()
conn
|> render("index.json",
for: for_user,
users: users,
as: :user,
embed_relationships: embed_relationships?(params)
)
end
@doc "POST /api/v1/accounts/:id/remove_from_followers"
def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
{:error, "Can not unfollow yourself"}
@ -631,7 +647,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
@doc "GET /api/v1/endorsements"
def endorsements(%{assigns: %{user: user}} = conn, params) do
def own_endorsements(%{assigns: %{user: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)

View file

@ -9,7 +9,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
only: [
json_response: 3,
add_link_headers: 2,
embed_relationships?: 1,
assign_account_by_id: 2
]
@ -45,12 +44,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
)
plug(
OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
when action == :endorsements
)
plug(
OAuthScopesPlug,
%{scopes: ["read:accounts"]} when action == :birthdays
@ -60,7 +53,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
plug(
:assign_account_by_id
when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
when action in [:favourites, :subscribe, :unsubscribe]
)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
@ -109,22 +102,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
)
end
@doc "GET /api/v1/pleroma/accounts/:id/endorsements"
def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
|> Pleroma.Repo.all()
conn
|> render("index.json",
for: for_user,
users: users,
as: :user,
embed_relationships: embed_relationships?(params)
)
end
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do

View file

@ -608,7 +608,6 @@ defmodule Pleroma.Web.Router do
scope [] do
pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites)
get("/accounts/:id/endorsements", AccountController, :endorsements)
get("/statuses/:id/quotes", StatusController, :quotes)
end
@ -637,6 +636,11 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/scrobbles", ScrobbleController, :index)
end
scope "/api/v1/pleroma", Pleroma.Web.MastodonAPI do
pipe_through(:api)
get("/accounts/:id/endorsements", AccountController, :endorsements)
end
scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
scope [] do
pipe_through(:authenticated_api)
@ -653,7 +657,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/relationships", AccountController, :relationships)
get("/accounts/familiar_followers", AccountController, :familiar_followers)
get("/accounts/:id/lists", AccountController, :lists)
get("/endorsements", AccountController, :endorsements)
get("/endorsements", AccountController, :own_endorsements)
get("/blocks", AccountController, :blocks)
get("/mutes", AccountController, :mutes)
@ -667,6 +671,8 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/note", AccountController, :note)
post("/accounts/:id/pin", AccountController, :endorse)
post("/accounts/:id/unpin", AccountController, :unendorse)
post("/accounts/:id/endorse", AccountController, :endorse)
post("/accounts/:id/unendorse", AccountController, :unendorse)
post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers)
get("/conversations", ConversationController, :index)
@ -782,6 +788,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/statuses", AccountController, :statuses)
get("/accounts/:id/followers", AccountController, :followers)
get("/accounts/:id/following", AccountController, :following)
get("/accounts/:id/endorsements", AccountController, :endorsements)
get("/accounts/:id", AccountController, :show)
post("/accounts", AccountController, :create)

View file

@ -2134,7 +2134,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert %{"id" => ^id1, "endorsed" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> post("/api/v1/accounts/#{id1}/endorse")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id1}] =
@ -2153,7 +2153,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert %{"id" => ^id1, "endorsed" => false} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/unpin")
|> post("/api/v1/accounts/#{id1}/unendorse")
|> json_response_and_validate_schema(200)
assert [] =
@ -2172,15 +2172,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> post("/api/v1/accounts/#{id1}/endorse")
|> json_response_and_validate_schema(200)
assert %{"error" => "You have already pinned the maximum number of users"} =
conn
|> assign(:user, user)
|> post("/api/v1/accounts/#{id2}/pin")
|> post("/api/v1/accounts/#{id2}/endorse")
|> json_response_and_validate_schema(400)
end
test "returns a list of pinned accounts", %{conn: conn} do
clear_config([:instance, :max_endorsed_users], 3)
%{id: id1} = user1 = insert(:user)
%{id: id2} = user2 = insert(:user)
%{id: id3} = user3 = insert(:user)
CommonAPI.follow(user2, user1)
CommonAPI.follow(user3, user1)
User.endorse(user1, user2)
User.endorse(user1, user3)
[%{"id" => ^id2}, %{"id" => ^id3}] =
conn
|> get("/api/v1/accounts/#{id1}/endorsements")
|> json_response_and_validate_schema(200)
end
test "returns 404 error when specified user is not exist", %{conn: conn} do
conn = get(conn, "/api/v1/accounts/test/endorsements")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "familiar followers" do

View file

@ -280,35 +280,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
end
end
describe "account endorsements" do
test "returns a list of pinned accounts", %{conn: conn} do
%{id: id1} = user1 = insert(:user)
%{id: id2} = user2 = insert(:user)
%{id: id3} = user3 = insert(:user)
CommonAPI.follow(user2, user1)
CommonAPI.follow(user3, user1)
User.endorse(user1, user2)
User.endorse(user1, user3)
response =
conn
|> get("/api/v1/pleroma/accounts/#{id1}/endorsements")
|> json_response_and_validate_schema(200)
assert length(response) == 2
assert Enum.any?(response, fn user -> user["id"] == id2 end)
assert Enum.any?(response, fn user -> user["id"] == id3 end)
end
test "returns 404 error when specified user is not exist", %{conn: conn} do
conn = get(conn, "/api/v1/pleroma/accounts/test/endorsements")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "birthday reminders" do
test "returns a list of friends having birthday on specified day" do
%{user: user, conn: conn} = oauth_access(["read:accounts"])