Merge branch 'account-endorsements' into 'develop'

Account endorsements

See merge request pleroma/pleroma!3601
This commit is contained in:
Alex Gleason 2022-01-13 19:02:21 +00:00
commit 84dcb55b0f
17 changed files with 343 additions and 27 deletions

View File

@ -258,7 +258,8 @@
show_reactions: true, show_reactions: true,
password_reset_token_validity: 60 * 60 * 24, password_reset_token_validity: 60 * 60 * 24,
profile_directory: true, profile_directory: true,
privileged_staff: false privileged_staff: false,
max_endorsed_users: 20
config :pleroma, :welcome, config :pleroma, :welcome,
direct_message: [ direct_message: [

View File

@ -742,6 +742,16 @@
3 3
] ]
}, },
%{
key: :max_endorsed_users,
type: :integer,
description: "The maximum number of recommended accounts. 0 will disable the feature.",
suggestions: [
0,
1,
3
]
},
%{ %{
key: :autofollowed_nicknames, key: :autofollowed_nicknames,
type: {:list, :string}, type: {:list, :string},

View File

@ -377,12 +377,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]` - `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
### Endorsements
*Added in Mastodon 2.5.0*
- `GET /api/v1/endorsements`: Returns an empty array, `[]`
### Featured tags ### Featured tags
*Added in Mastodon 3.0.0* *Added in Mastodon 3.0.0*

View File

@ -10,7 +10,8 @@
reblog_mute: 3, reblog_mute: 3,
notification_mute: 4, notification_mute: 4,
inverse_subscription: 5, inverse_subscription: 5,
suggestion_dismiss: 6 suggestion_dismiss: 6,
endorsement: 7
) )
defenum(Pleroma.FollowingRelationship.State, defenum(Pleroma.FollowingRelationship.State,

View File

@ -78,6 +78,10 @@ defmodule Pleroma.User do
inverse_subscription: [ inverse_subscription: [
subscribee_subscriptions: :subscriber_users, subscribee_subscriptions: :subscriber_users,
subscriber_subscriptions: :subscribee_users subscriber_subscriptions: :subscribee_users
],
endorsement: [
endorser_endorsements: :endorsed_users,
endorsee_endorsements: :endorser_users
] ]
] ]
@ -170,25 +174,25 @@ defmodule Pleroma.User do
{incoming_relation, incoming_relation_source} {incoming_relation, incoming_relation_source}
]} <- @user_relationships_config do ]} <- @user_relationships_config do
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes, # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
# :notification_muter_mutes, :subscribee_subscriptions # :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
has_many(outgoing_relation, UserRelationship, has_many(outgoing_relation, UserRelationship,
foreign_key: :source_id, foreign_key: :source_id,
where: [relationship_type: relationship_type] where: [relationship_type: relationship_type]
) )
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes, # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
# :notification_mutee_mutes, :subscriber_subscriptions # :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
has_many(incoming_relation, UserRelationship, has_many(incoming_relation, UserRelationship,
foreign_key: :target_id, foreign_key: :target_id,
where: [relationship_type: relationship_type] where: [relationship_type: relationship_type]
) )
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users, # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
# :notification_muted_users, :subscriber_users # :notification_muted_users, :subscriber_users, :endorsed_users
has_many(outgoing_relation_target, through: [outgoing_relation, :target]) has_many(outgoing_relation_target, through: [outgoing_relation, :target])
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users, # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
# :notification_muter_users, :subscribee_users # :notification_muter_users, :subscribee_users, :endorser_users
has_many(incoming_relation_source, through: [incoming_relation, :source]) has_many(incoming_relation_source, through: [incoming_relation, :source])
end end
@ -216,7 +220,7 @@ defmodule Pleroma.User do
@user_relationships_config do @user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`, # `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`, # `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
# `def subscriber_users/2` # `def subscriber_users/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target)) target_users_query = assoc(user, unquote(outgoing_relation_target))
@ -229,7 +233,7 @@ def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated?
end end
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`, # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
# `def notification_muted_users/2`, `def subscriber_users/2` # `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
__MODULE__ __MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@ -240,7 +244,8 @@ def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
end end
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`, # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2` # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
# `def endorsed_users_ap_ids/2`
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
__MODULE__ __MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@ -1516,6 +1521,40 @@ def unblock(%User{} = blocker, %{ap_id: ap_id}) do
unblock(blocker, get_cached_by_ap_id(ap_id)) unblock(blocker, get_cached_by_ap_id(ap_id))
end end
def endorse(%User{} = endorser, %User{} = target) do
with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
endorsed_users <-
User.endorsed_users_relation(endorser)
|> Repo.aggregate(:count, :id) do
cond do
endorsed_users >= max_endorsed_users ->
{:error, "You have already pinned the maximum number of users"}
not following?(endorser, target) ->
{:error, "Could not endorse: You are not following #{target.nickname}"}
true ->
UserRelationship.create_endorsement(endorser, target)
end
end
end
def endorse(%User{} = endorser, %{ap_id: ap_id}) do
with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
endorse(endorser, endorsed)
end
end
def unendorse(%User{} = unendorser, %User{} = target) do
UserRelationship.delete_endorsement(unendorser, target)
end
def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
with %User{} = user <- get_cached_by_ap_id(ap_id) do
unendorse(unendorser, user)
end
end
def mutes?(nil, _), do: false def mutes?(nil, _), do: false
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target) def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
@ -1561,6 +1600,10 @@ def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
end end
end end
def endorses?(%User{} = user, %User{} = target) do
UserRelationship.endorsement_exists?(user, target)
end
@doc """ @doc """
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type. Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}` E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`

View File

@ -24,17 +24,20 @@ defmodule Pleroma.UserRelationship do
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
# `def create_notification_mute/2`, `def create_inverse_subscription/2` # `def create_notification_mute/2`, `def create_inverse_subscription/2`,
# `def endorsement/2`
def unquote(:"create_#{relationship_type}")(source, target), def unquote(:"create_#{relationship_type}")(source, target),
do: create(unquote(relationship_type), source, target) do: create(unquote(relationship_type), source, target)
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`, # `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2` # `def delete_notification_mute/2`, `def delete_inverse_subscription/2`,
# `def delete_endorsement/2`
def unquote(:"delete_#{relationship_type}")(source, target), def unquote(:"delete_#{relationship_type}")(source, target),
do: delete(unquote(relationship_type), source, target) do: delete(unquote(relationship_type), source, target)
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`, # `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2` # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`,
# `def inverse_endorsement?/2`
def unquote(:"#{relationship_type}_exists?")(source, target), def unquote(:"#{relationship_type}_exists?")(source, target),
do: exists?(unquote(relationship_type), source, target) do: exists?(unquote(relationship_type), source, target)
end end

View File

@ -334,6 +334,42 @@ def unblock_operation do
} }
end end
def endorse_operation do
%Operation{
tags: ["Account actions"],
summary: "Endorse",
operationId: "AccountController.endorse",
security: [%{"oAuth" => ["follow", "write:accounts"]}],
description: "Addds the given account to endorsed accounts list.",
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{
200 => Operation.response("Relationship", "application/json", AccountRelationship),
400 =>
Operation.response("Bad Request", "application/json", %Schema{
allOf: [ApiError],
title: "Unprocessable Entity",
example: %{
"error" => "You have already pinned the maximum number of users"
}
})
}
}
end
def unendorse_operation do
%Operation{
tags: ["Account actions"],
summary: "Unendorse",
operationId: "AccountController.unendorse",
security: [%{"oAuth" => ["follow", "write:accounts"]}],
description: "Removes the given account from endorsed accounts list.",
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{
200 => Operation.response("Relationship", "application/json", AccountRelationship)
}
}
end
def note_operation do def note_operation do
%Operation{ %Operation{
tags: ["Account actions"], tags: ["Account actions"],
@ -425,10 +461,10 @@ def endorsements_operation do
tags: ["Retrieve account information"], tags: ["Retrieve account information"],
summary: "Endorsements", summary: "Endorsements",
operationId: "AccountController.endorsements", operationId: "AccountController.endorsements",
description: "Not implemented", description: "Returns endorsed accounts",
security: [%{"oAuth" => ["read:accounts"]}], security: [%{"oAuth" => ["read:accounts"]}],
responses: %{ responses: %{
200 => empty_array_response() 200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
} }
} }
end end

View File

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
alias OpenApiSpex.Operation alias OpenApiSpex.Operation
alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.FlakeID
@ -62,6 +63,25 @@ def favourites_operation do
} }
end 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 def subscribe_operation do
%Operation{ %Operation{
tags: ["Account actions"], tags: ["Account actions"],

View File

@ -117,7 +117,8 @@ def follow(follower, followed) do
def unfollow(follower, unfollowed) do def unfollow(follower, unfollowed) do
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed), with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed), {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
{:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do {:ok, _subscription} <- User.unsubscribe(follower, unfollowed),
{:ok, _endorsement} <- User.unendorse(follower, unfollowed) do
{:ok, follower} {:ok, follower}
end end
end end

View File

@ -57,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["write:accounts"]} %{scopes: ["write:accounts"]}
when action in [:update_credentials, :note] when action in [:update_credentials, :note, :endorse, :unendorse]
) )
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
@ -84,7 +84,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
@relationship_actions [:follow, :unfollow] @relationship_actions [:follow, :unfollow]
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a @needs_account ~W(
followers following lists follow unfollow mute unmute block unblock note endorse unendorse
)a
plug( plug(
RateLimiter, RateLimiter,
@ -450,6 +452,24 @@ def note(
end end
end end
@doc "POST /api/v1/accounts/:id/pin"
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
render(conn, "relationship.json", user: endorser, target: endorsed)
else
{:error, message} -> json_response(conn, :bad_request, %{error: message})
end
end
@doc "POST /api/v1/accounts/:id/unpin"
def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do
render(conn, "relationship.json", user: endorser, target: endorsed)
else
{:error, message} -> json_response(conn, :forbidden, %{error: message})
end
end
@doc "POST /api/v1/follows" @doc "POST /api/v1/follows"
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
case User.get_cached_by_nickname(uri) do case User.get_cached_by_nickname(uri) do
@ -505,7 +525,20 @@ def lookup(conn, %{acct: nickname} = _params) do
end end
@doc "GET /api/v1/endorsements" @doc "GET /api/v1/endorsements"
def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params) def endorsements(%{assigns: %{user: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
|> Pleroma.Repo.all()
conn
|> render("index.json",
users: users,
for: user,
as: :user,
embed_relationships: embed_relationships?(params)
)
end
@doc "GET /api/v1/identity_proofs" @doc "GET /api/v1/identity_proofs"
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params) def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)

View File

@ -160,11 +160,18 @@ def render(
target, target,
&User.muting_reblogs?(&1, &2) &User.muting_reblogs?(&1, &2)
), ),
endorsed: false,
note: note:
UserNote.show( UserNote.show(
reading_user, reading_user,
target target
),
endorsed:
UserRelationship.exists?(
user_relationships,
:endorsement,
target,
reading_user,
&User.endorses?(&2, &1)
) )
} }
end end

View File

@ -6,7 +6,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] only: [
json_response: 3,
add_link_headers: 2,
embed_relationships?: 1,
assign_account_by_id: 2
]
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
@ -40,9 +45,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites %{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
) )
plug(
OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
when action == :endorsements
)
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend) plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe]) plug(
:assign_account_by_id
when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
@ -90,6 +104,22 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
) )
end 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" @doc "POST /api/v1/pleroma/accounts/:id/subscribe"
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do with {:ok, _subscription} <- User.subscribe(user, subscription_target) do

View File

@ -440,6 +440,7 @@ defmodule Pleroma.Web.Router do
scope [] do scope [] do
pipe_through(:api) pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites) get("/accounts/:id/favourites", AccountController, :favourites)
get("/accounts/:id/endorsements", AccountController, :endorsements)
end end
scope [] do scope [] do
@ -486,6 +487,8 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/mute", AccountController, :mute)
post("/accounts/:id/unmute", AccountController, :unmute) post("/accounts/:id/unmute", AccountController, :unmute)
post("/accounts/:id/note", AccountController, :note) post("/accounts/:id/note", AccountController, :note)
post("/accounts/:id/pin", AccountController, :endorse)
post("/accounts/:id/unpin", AccountController, :unendorse)
get("/conversations", ConversationController, :index) get("/conversations", ConversationController, :index)
post("/conversations/:id/read", ConversationController, :mark_as_read) post("/conversations/:id/read", ConversationController, :mark_as_read)

View File

@ -2498,4 +2498,39 @@ defp object_id_from_created_activity(user) do
%{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id) %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
object_id object_id
end end
describe "account endorsements" do
test "it pins people" do
user = insert(:user)
pinned_user = insert(:user)
{:ok, _pinned_user, _user} = User.follow(user, pinned_user)
refute User.endorses?(user, pinned_user)
{:ok, _user_relationship} = User.endorse(user, pinned_user)
assert User.endorses?(user, pinned_user)
end
test "it unpins users" do
user = insert(:user)
pinned_user = insert(:user)
{:ok, _pinned_user, _user} = User.follow(user, pinned_user)
{:ok, _user_relationship} = User.endorse(user, pinned_user)
{:ok, _user_pin} = User.unendorse(user, pinned_user)
refute User.endorses?(user, pinned_user)
end
test "it doesn't pin users you do not follow" do
user = insert(:user)
pinned_user = insert(:user)
assert {:error, _message} = User.endorse(user, pinned_user)
refute User.endorses?(user, pinned_user)
end
end
end end

View File

@ -1207,6 +1207,18 @@ test "also unsubscribes a user" do
refute User.subscribed_to?(follower, followed) refute User.subscribed_to?(follower, followed)
end end
test "also unpins a user" do
[follower, followed] = insert_pair(:user)
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
{:ok, _endorsement} = User.endorse(follower, followed)
assert User.endorses?(follower, followed)
{:ok, follower} = CommonAPI.unfollow(follower, followed)
refute User.endorses?(follower, followed)
end
test "cancels a pending follow for a local user" do test "cancels a pending follow for a local user" do
follower = insert(:user) follower = insert(:user)
followed = insert(:user, is_locked: true) followed = insert(:user, is_locked: true)

View File

@ -1838,4 +1838,66 @@ test "create a note on a user" do
|> get("/api/v1/accounts/relationships?id=#{other_user.id}") |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)
end end
describe "account endorsements" do
setup do: oauth_access(["read:accounts", "write:accounts", "write:follows"])
setup do: clear_config([:instance, :max_endorsed_users], 1)
test "pin account", %{user: user, conn: conn} do
%{id: id1} = other_user1 = insert(:user)
CommonAPI.follow(user, other_user1)
assert %{"id" => ^id1, "endorsed" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id1}] =
conn
|> put_req_header("content-type", "application/json")
|> get("/api/v1/endorsements")
|> json_response_and_validate_schema(200)
end
test "unpin account", %{user: user, conn: conn} do
%{id: id1} = other_user1 = insert(:user)
CommonAPI.follow(user, other_user1)
User.endorse(user, other_user1)
assert %{"id" => ^id1, "endorsed" => false} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/unpin")
|> json_response_and_validate_schema(200)
assert [] =
conn
|> put_req_header("content-type", "application/json")
|> get("/api/v1/endorsements")
|> json_response_and_validate_schema(200)
end
test "max pinned accounts", %{user: user, conn: conn} do
%{id: id1} = other_user1 = insert(:user)
%{id: id2} = other_user2 = insert(:user)
CommonAPI.follow(user, other_user1)
CommonAPI.follow(user, other_user2)
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> 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")
|> json_response_and_validate_schema(400)
end
end
end end

View File

@ -279,4 +279,29 @@ test "returns 404 when subscription_target not found" do
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
end end
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(user1, user2)
CommonAPI.follow(user1, user3)
User.endorse(user1, user2)
User.endorse(user1, user3)
[%{"id" => ^id2}, %{"id" => ^id3}] =
conn
|> get("/api/v1/pleroma/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/pleroma/accounts/test/endorsements")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
end end