Extract account actions from `MastodonAPIController` to `AccountController` See merge request pleroma/pleroma!1731object-id-column
@@ -84,22 +84,11 @@ defmodule Pleroma.List do | |||
end | |||
# Get lists to which the account belongs. | |||
def get_lists_account_belongs(%User{} = owner, account_id) do | |||
user = User.get_cached_by_id(account_id) | |||
query = | |||
from( | |||
l in Pleroma.List, | |||
where: | |||
l.user_id == ^owner.id and | |||
fragment( | |||
"? = ANY(?)", | |||
^user.follower_address, | |||
l.following | |||
) | |||
) | |||
Repo.all(query) | |||
def get_lists_account_belongs(%User{} = owner, user) do | |||
Pleroma.List | |||
|> where([l], l.user_id == ^owner.id) | |||
|> where([l], fragment("? = ANY(?)", ^user.follower_address, l.following)) | |||
|> Repo.all() | |||
end | |||
def rename(%Pleroma.List{} = list, title) do | |||
@@ -43,7 +43,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do | |||
end | |||
defp merge_account_views(%User{} = user) do | |||
Pleroma.Web.MastodonAPI.AccountView.render("account.json", %{user: user}) | |||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user}) | |||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})) | |||
end | |||
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.ChatChannel do | |||
if String.length(text) > 0 do | |||
author = User.get_cached_by_nickname(user_name) | |||
author = Pleroma.Web.MastodonAPI.AccountView.render("account.json", user: author) | |||
author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author) | |||
message = ChatChannelState.add_message(%{text: text, author: author}) | |||
broadcast!(socket, "new_msg", message) | |||
@@ -68,4 +68,11 @@ defmodule Pleroma.Web.ControllerHelper do | |||
conn | |||
end | |||
end | |||
def assign_account_by_id(%{params: %{"id" => id}} = conn, _) do | |||
case Pleroma.User.get_cached_by_id(id) do | |||
%Pleroma.User{} = account -> assign(conn, :account, account) | |||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() | |||
end | |||
end | |||
end |
@@ -0,0 +1,304 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.AccountController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, | |||
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3] | |||
alias Pleroma.Emoji | |||
alias Pleroma.Plugs.RateLimiter | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.ListView | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.OAuth.Token | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
@relations [:follow, :unfollow] | |||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a | |||
plug(RateLimiter, {:relations_id_action, params: ["id", "uri"]} when action in @relations) | |||
plug(RateLimiter, :relations_actions when action in @relations) | |||
plug(RateLimiter, :app_account_creation when action == :create) | |||
plug(:assign_account_by_id when action in @needs_account) | |||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController) | |||
@doc "POST /api/v1/accounts" | |||
def create( | |||
%{assigns: %{app: app}} = conn, | |||
%{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params | |||
) do | |||
params = | |||
params | |||
|> Map.take([ | |||
"email", | |||
"captcha_solution", | |||
"captcha_token", | |||
"captcha_answer_data", | |||
"token", | |||
"password" | |||
]) | |||
|> Map.put("nickname", nickname) | |||
|> Map.put("fullname", params["fullname"] || nickname) | |||
|> Map.put("bio", params["bio"] || "") | |||
|> Map.put("confirm", params["password"]) | |||
with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), | |||
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do | |||
json(conn, %{ | |||
token_type: "Bearer", | |||
access_token: token.token, | |||
scope: app.scopes, | |||
created_at: Token.Utils.format_created_at(token) | |||
}) | |||
else | |||
{:error, errors} -> json_response(conn, :bad_request, errors) | |||
end | |||
end | |||
def create(%{assigns: %{app: _app}} = conn, _) do | |||
render_error(conn, :bad_request, "Missing parameters") | |||
end | |||
def create(conn, _) do | |||
render_error(conn, :forbidden, "Invalid credentials") | |||
end | |||
@doc "GET /api/v1/accounts/verify_credentials" | |||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do | |||
chat_token = Phoenix.Token.sign(conn, "user socket", user.id) | |||
render(conn, "show.json", | |||
user: user, | |||
for: user, | |||
with_pleroma_settings: true, | |||
with_chat_token: chat_token | |||
) | |||
end | |||
@doc "PATCH /api/v1/accounts/update_credentials" | |||
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do | |||
user = original_user | |||
user_params = | |||
%{} | |||
|> add_if_present(params, "display_name", :name) | |||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end) | |||
|> add_if_present(params, "avatar", :avatar, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do | |||
{:ok, object.data} | |||
end | |||
end) | |||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") | |||
user_info_emojis = | |||
user.info | |||
|> Map.get(:emoji, []) | |||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text)) | |||
|> Enum.dedup() | |||
info_params = | |||
[ | |||
:no_rich_text, | |||
:locked, | |||
:hide_followers_count, | |||
:hide_follows_count, | |||
:hide_followers, | |||
:hide_follows, | |||
:hide_favorites, | |||
:show_role, | |||
:skip_thread_containment, | |||
:discoverable | |||
] | |||
|> Enum.reduce(%{}, fn key, acc -> | |||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)}) | |||
end) | |||
|> add_if_present(params, "default_scope", :default_scope) | |||
|> add_if_present(params, "fields", :fields, fn fields -> | |||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) | |||
{:ok, fields} | |||
end) | |||
|> add_if_present(params, "fields", :raw_fields) | |||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> | |||
{:ok, Map.merge(user.info.pleroma_settings_store, value)} | |||
end) | |||
|> add_if_present(params, "header", :banner, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do | |||
{:ok, object.data} | |||
end | |||
end) | |||
|> add_if_present(params, "pleroma_background_image", :background, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :background) do | |||
{:ok, object.data} | |||
end | |||
end) | |||
|> Map.put(:emoji, user_info_emojis) | |||
changeset = | |||
user | |||
|> User.update_changeset(user_params) | |||
|> User.change_info(&User.Info.profile_update(&1, info_params)) | |||
with {:ok, user} <- User.update_and_set_cache(changeset) do | |||
if original_user != user, do: CommonAPI.update(user) | |||
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true) | |||
else | |||
_e -> render_error(conn, :forbidden, "Invalid request") | |||
end | |||
end | |||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do | |||
with true <- Map.has_key?(params, params_field), | |||
{:ok, new_value} <- value_function.(params[params_field]) do | |||
Map.put(map, map_field, new_value) | |||
else | |||
_ -> map | |||
end | |||
end | |||
@doc "GET /api/v1/accounts/relationships" | |||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
targets = User.get_all_by_ids(List.wrap(id)) | |||
render(conn, "relationships.json", user: user, targets: targets) | |||
end | |||
# Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array. | |||
def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, []) | |||
@doc "GET /api/v1/accounts/:id" | |||
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do | |||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), | |||
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do | |||
render(conn, "show.json", user: user, for: for_user) | |||
else | |||
_e -> render_error(conn, :not_found, "Can't find user") | |||
end | |||
end | |||
@doc "GET /api/v1/accounts/:id/statuses" | |||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do | |||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do | |||
params = Map.put(params, "tag", params["tagged"]) | |||
activities = ActivityPub.fetch_user_activities(user, reading_user, params) | |||
conn | |||
|> add_link_headers(activities) | |||
|> put_view(StatusView) | |||
|> render("index.json", activities: activities, for: reading_user, as: :activity) | |||
end | |||
end | |||
@doc "GET /api/v1/accounts/:id/followers" | |||
def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do | |||
followers = | |||
cond do | |||
for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params) | |||
user.info.hide_followers -> [] | |||
true -> MastodonAPI.get_followers(user, params) | |||
end | |||
conn | |||
|> add_link_headers(followers) | |||
|> render("index.json", for: for_user, users: followers, as: :user) | |||
end | |||
@doc "GET /api/v1/accounts/:id/following" | |||
def following(%{assigns: %{user: for_user, account: user}} = conn, params) do | |||
followers = | |||
cond do | |||
for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params) | |||
user.info.hide_follows -> [] | |||
true -> MastodonAPI.get_friends(user, params) | |||
end | |||
conn | |||
|> add_link_headers(followers) | |||
|> render("index.json", for: for_user, users: followers, as: :user) | |||
end | |||
@doc "GET /api/v1/accounts/:id/lists" | |||
def lists(%{assigns: %{user: user, account: account}} = conn, _params) do | |||
lists = Pleroma.List.get_lists_account_belongs(user, account) | |||
conn | |||
|> put_view(ListView) | |||
|> render("index.json", lists: lists) | |||
end | |||
@doc "POST /api/v1/accounts/:id/follow" | |||
def follow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do | |||
{:error, :not_found} | |||
end | |||
def follow(%{assigns: %{user: follower, account: followed}} = conn, _params) do | |||
with {:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do | |||
render(conn, "relationship.json", user: follower, target: followed) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
@doc "POST /api/v1/accounts/:id/unfollow" | |||
def unfollow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do | |||
{:error, :not_found} | |||
end | |||
def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) do | |||
with {:ok, follower} <- CommonAPI.unfollow(follower, followed) do | |||
render(conn, "relationship.json", user: follower, target: followed) | |||
end | |||
end | |||
@doc "POST /api/v1/accounts/:id/mute" | |||
def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do | |||
notifications? = params |> Map.get("notifications", true) |> truthy_param?() | |||
with {:ok, muter} <- User.mute(muter, muted, notifications?) do | |||
render(conn, "relationship.json", user: muter, target: muted) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
@doc "POST /api/v1/accounts/:id/unmute" | |||
def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do | |||
with {:ok, muter} <- User.unmute(muter, muted) do | |||
render(conn, "relationship.json", user: muter, target: muted) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
@doc "POST /api/v1/accounts/:id/block" | |||
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do | |||
with {:ok, blocker} <- User.block(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do | |||
render(conn, "relationship.json", user: blocker, target: blocked) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
@doc "POST /api/v1/accounts/:id/unblock" | |||
def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do | |||
with {:ok, blocker} <- User.unblock(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do | |||
render(conn, "relationship.json", user: blocker, target: blocked) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
end |
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do | |||
def index(%{assigns: %{user: followed}} = conn, _params) do | |||
follow_requests = User.get_follow_requests(followed) | |||
render(conn, "accounts.json", for: followed, users: follow_requests, as: :user) | |||
render(conn, "index.json", for: followed, users: follow_requests, as: :user) | |||
end | |||
@doc "POST /api/v1/follow_requests/:id/authorize" | |||
@@ -49,7 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do | |||
with {:ok, users} <- Pleroma.List.get_following(list) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("accounts.json", for: user, users: users, as: :user) | |||
|> render("index.json", for: user, users: users, as: :user) | |||
end | |||
end | |||
@@ -5,14 +5,11 @@ | |||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, | |||
only: [json_response: 3, add_link_headers: 2, truthy_param?: 1] | |||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] | |||
alias Ecto.Changeset | |||
alias Pleroma.Activity | |||
alias Pleroma.Bookmark | |||
alias Pleroma.Config | |||
alias Pleroma.Emoji | |||
alias Pleroma.HTTP | |||
alias Pleroma.Object | |||
alias Pleroma.Pagination | |||
@@ -26,8 +23,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MastodonAPI.AppView | |||
alias Pleroma.Web.MastodonAPI.ListView | |||
alias Pleroma.Web.MastodonAPI.MastodonAPI | |||
alias Pleroma.Web.MastodonAPI.MastodonView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.MediaProxy | |||
@@ -38,20 +33,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Web.TwitterAPI.TwitterAPI | |||
require Logger | |||
require Pleroma.Constants | |||
@rate_limited_relations_actions ~w(follow unfollow)a | |||
plug( | |||
RateLimiter, | |||
{:relations_id_action, params: ["id", "uri"]} when action in @rate_limited_relations_actions | |||
) | |||
plug(RateLimiter, :relations_actions when action in @rate_limited_relations_actions) | |||
plug(RateLimiter, :app_account_creation when action == :account_register) | |||
plug(RateLimiter, :search when action in [:search, :search2, :account_search]) | |||
plug(RateLimiter, :password_reset when action == :password_reset) | |||
plug(RateLimiter, :account_confirmation_resend when action == :account_confirmation_resend) | |||
@local_mastodon_name "Mastodon-Local" | |||
@@ -74,180 +57,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
defp add_if_present( | |||
map, | |||
params, | |||
params_field, | |||
map_field, | |||
value_function \\ fn x -> {:ok, x} end | |||
) do | |||
if Map.has_key?(params, params_field) do | |||
case value_function.(params[params_field]) do | |||
{:ok, new_value} -> Map.put(map, map_field, new_value) | |||
:error -> map | |||
end | |||
else | |||
map | |||
end | |||
end | |||
def update_credentials(%{assigns: %{user: user}} = conn, params) do | |||
original_user = user | |||
user_params = | |||
%{} | |||
|> add_if_present(params, "display_name", :name) | |||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end) | |||
|> add_if_present(params, "avatar", :avatar, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do | |||
{:ok, object.data} | |||
else | |||
_ -> :error | |||
end | |||
end) | |||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") | |||
user_info_emojis = | |||
user.info | |||
|> Map.get(:emoji, []) | |||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text)) | |||
|> Enum.dedup() | |||
info_params = | |||
[ | |||
:no_rich_text, | |||
:locked, | |||
:hide_followers_count, | |||
:hide_follows_count, | |||
:hide_followers, | |||
:hide_follows, | |||
:hide_favorites, | |||
:show_role, | |||
:skip_thread_containment, | |||
:discoverable | |||
] | |||
|> Enum.reduce(%{}, fn key, acc -> | |||
add_if_present(acc, params, to_string(key), key, fn value -> | |||
{:ok, truthy_param?(value)} | |||
end) | |||
end) | |||
|> add_if_present(params, "default_scope", :default_scope) | |||
|> add_if_present(params, "fields", :fields, fn fields -> | |||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) | |||
{:ok, fields} | |||
end) | |||
|> add_if_present(params, "fields", :raw_fields) | |||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> | |||
{:ok, Map.merge(user.info.pleroma_settings_store, value)} | |||
end) | |||
|> add_if_present(params, "header", :banner, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :banner) do | |||
{:ok, object.data} | |||
else | |||
_ -> :error | |||
end | |||
end) | |||
|> add_if_present(params, "pleroma_background_image", :background, fn value -> | |||
with %Plug.Upload{} <- value, | |||
{:ok, object} <- ActivityPub.upload(value, type: :background) do | |||
{:ok, object.data} | |||
else | |||
_ -> :error | |||
end | |||
end) | |||
|> Map.put(:emoji, user_info_emojis) | |||
changeset = | |||
user | |||
|> User.update_changeset(user_params) | |||
|> User.change_info(&User.Info.profile_update(&1, info_params)) | |||
with {:ok, user} <- User.update_and_set_cache(changeset) do | |||
if original_user != user, do: CommonAPI.update(user) | |||
json( | |||
conn, | |||
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true}) | |||
) | |||
else | |||
_e -> render_error(conn, :forbidden, "Invalid request") | |||
end | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
change = Changeset.change(user, %{avatar: nil}) | |||
{:ok, user} = User.update_and_set_cache(change) | |||
CommonAPI.update(user) | |||
json(conn, %{url: nil}) | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, params) do | |||
{:ok, object} = ActivityPub.upload(params, type: :avatar) | |||
change = Changeset.change(user, %{avatar: object.data}) | |||
{:ok, user} = User.update_and_set_cache(change) | |||
CommonAPI.update(user) | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
json(conn, %{url: href}) | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do | |||
new_info = %{"banner" => %{}} | |||
with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
CommonAPI.update(user) | |||
json(conn, %{url: nil}) | |||
end | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), | |||
new_info <- %{"banner" => object.data}, | |||
{:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
CommonAPI.update(user) | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
json(conn, %{url: href}) | |||
end | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
new_info = %{"background" => %{}} | |||
with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
json(conn, %{url: nil}) | |||
end | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(params, type: :background), | |||
new_info <- %{"background" => object.data}, | |||
{:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
json(conn, %{url: href}) | |||
end | |||
end | |||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do | |||
chat_token = Phoenix.Token.sign(conn, "user socket", user.id) | |||
account = | |||
AccountView.render("account.json", %{ | |||
user: user, | |||
for: user, | |||
with_pleroma_settings: true, | |||
with_chat_token: chat_token | |||
}) | |||
json(conn, account) | |||
end | |||
def verify_app_credentials(%{assigns: %{user: _user, token: token}} = conn, _) do | |||
with %Token{app: %App{} = app} <- Repo.preload(token, :app) do | |||
conn | |||
@@ -256,16 +65,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do | |||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), | |||
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do | |||
account = AccountView.render("account.json", %{user: user, for: for_user}) | |||
json(conn, account) | |||
else | |||
_e -> render_error(conn, :not_found, "Can't find user") | |||
end | |||
end | |||
@mastodon_api_level "2.7.2" | |||
def masto_instance(conn, _params) do | |||
@@ -318,25 +117,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
json(conn, mastodon_emoji) | |||
end | |||
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do | |||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do | |||
params = | |||
params | |||
|> Map.put("tag", params["tagged"]) | |||
activities = ActivityPub.fetch_user_activities(user, reading_user, params) | |||
conn | |||
|> add_link_headers(activities) | |||
|> put_view(StatusView) | |||
|> render("index.json", %{ | |||
activities: activities, | |||
for: reading_user, | |||
as: :activity | |||
}) | |||
end | |||
end | |||
def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), | |||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), | |||
@@ -387,17 +167,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
targets = User.get_all_by_ids(List.wrap(id)) | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationships.json", %{user: user, targets: targets}) | |||
end | |||
# Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array. | |||
def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, []) | |||
def update_media( | |||
%{assigns: %{user: user}} = conn, | |||
%{"id" => id, "description" => description} = _ | |||
@@ -453,118 +222,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
json(conn, mascot) | |||
end | |||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do | |||
with %User{} = user <- User.get_cached_by_id(id), | |||
followers <- MastodonAPI.get_followers(user, params) do | |||
followers = | |||
cond do | |||
for_user && user.id == for_user.id -> followers | |||
user.info.hide_followers -> [] | |||
true -> followers | |||
end | |||
conn | |||
|> add_link_headers(followers) | |||
|> put_view(AccountView) | |||
|> render("accounts.json", %{for: for_user, users: followers, as: :user}) | |||
end | |||
end | |||
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do | |||
with %User{} = user <- User.get_cached_by_id(id), | |||
followers <- MastodonAPI.get_friends(user, params) do | |||
followers = | |||
cond do | |||
for_user && user.id == for_user.id -> followers | |||
user.info.hide_follows -> [] | |||
true -> followers | |||
end | |||
conn | |||
|> add_link_headers(followers) | |||
|> put_view(AccountView) | |||
|> render("accounts.json", %{for: for_user, users: followers, as: :user}) | |||
end | |||
end | |||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do | |||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)}, | |||
{_, true} <- {:followed, follower.id != followed.id}, | |||
{:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: follower, target: followed}) | |||
else | |||
{:followed, _} -> | |||
{:error, :not_found} | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do | |||
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do | |||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, | |||
{_, true} <- {:followed, follower.id != followed.id}, | |||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("account.json", %{user: followed, for: follower}) | |||
else | |||
{:followed, _} -> | |||
{:error, :not_found} | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do | |||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_id(id)}, | |||
{_, true} <- {:followed, follower.id != followed.id}, | |||
{:ok, follower} <- CommonAPI.unfollow(follower, followed) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: follower, target: followed}) | |||
|> render("show.json", %{user: followed, for: follower}) | |||
else | |||
{:followed, _} -> | |||
{:error, :not_found} | |||
error -> | |||
error | |||
end | |||
end | |||
def mute(%{assigns: %{user: muter}} = conn, %{"id" => id} = params) do | |||
notifications = | |||
if Map.has_key?(params, "notifications"), | |||
do: params["notifications"] in [true, "True", "true", "1"], | |||
else: true | |||
with %User{} = muted <- User.get_cached_by_id(id), | |||
{:ok, muter} <- User.mute(muter, muted, notifications) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: muter, target: muted}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do | |||
with %User{} = muted <- User.get_cached_by_id(id), | |||
{:ok, muter} <- User.unmute(muter, muted) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: muter, target: muted}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
@@ -574,72 +242,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
def mutes(%{assigns: %{user: user}} = conn, _) do | |||
with muted_accounts <- User.muted_users(user) do | |||
res = AccountView.render("accounts.json", users: muted_accounts, for: user, as: :user) | |||
res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user) | |||
json(conn, res) | |||
end | |||
end | |||
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do | |||
with %User{} = blocked <- User.get_cached_by_id(id), | |||
{:ok, blocker} <- User.block(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: blocker, target: blocked}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do | |||
with %User{} = blocked <- User.get_cached_by_id(id), | |||
{:ok, blocker} <- User.unblock(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: blocker, target: blocked}) | |||
else | |||
{:error, message} -> | |||
conn | |||
|> put_status(:forbidden) | |||
|> json(%{error: message}) | |||
end | |||
end | |||
def blocks(%{assigns: %{user: user}} = conn, _) do | |||
with blocked_accounts <- User.blocked_users(user) do | |||
res = AccountView.render("accounts.json", users: blocked_accounts, for: user, as: :user) | |||
res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user) | |||
json(conn, res) | |||
end | |||
end | |||
def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %User{} = subscription_target <- User.get_cached_by_id(id), | |||
{:ok, subscription_target} = User.subscribe(user, subscription_target) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: user, target: subscription_target}) | |||
else | |||
nil -> {:error, :not_found} | |||
e -> e | |||
end | |||
end | |||
def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do | |||
with %User{} = subscription_target <- User.get_cached_by_id(id), | |||
{:ok, subscription_target} = User.unsubscribe(user, subscription_target) do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("relationship.json", %{user: user, target: subscription_target}) | |||
else | |||
nil -> {:error, :not_found} | |||
e -> e | |||
end | |||
end | |||
def favourites(%{assigns: %{user: user}} = conn, params) do | |||
params = | |||
params | |||
@@ -657,37 +271,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> render("index.json", %{activities: activities, for: user, as: :activity}) | |||
end | |||
def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do | |||
with %User{} = user <- User.get_by_id(id), | |||
false <- user.info.hide_favorites do | |||
params = | |||
params | |||
|> Map.put("type", "Create") | |||
|> Map.put("favorited_by", user.ap_id) | |||
|> Map.put("blocking_user", for_user) | |||
recipients = | |||
if for_user do | |||
[Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following] | |||
else | |||
[Pleroma.Constants.as_public()] | |||
end | |||
activities = | |||
recipients | |||
|> ActivityPub.fetch_activities(params) | |||
|> Enum.reverse() | |||
conn | |||
|> add_link_headers(activities) | |||
|> put_view(StatusView) | |||
|> render("index.json", %{activities: activities, for: for_user, as: :activity}) | |||
else | |||
nil -> {:error, :not_found} | |||
true -> render_error(conn, :forbidden, "Can't get favorites") | |||
end | |||
end | |||
def bookmarks(%{assigns: %{user: user}} = conn, params) do | |||
user = User.get_cached_by_id(user.id) | |||
@@ -705,14 +288,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
|> render("index.json", %{activities: activities, for: user, as: :activity}) | |||
end | |||
def account_lists(%{assigns: %{user: user}} = conn, %{"id" => account_id}) do | |||
lists = Pleroma.List.get_lists_account_belongs(user, account_id) | |||
conn | |||
|> put_view(ListView) | |||
|> render("index.json", %{lists: lists}) | |||
end | |||
def index(%{assigns: %{user: user}} = conn, _params) do | |||
token = get_session(conn, :oauth_token) | |||
@@ -721,8 +296,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
limit = Config.get([:instance, :limit]) | |||
accounts = | |||
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user})) | |||
accounts = Map.put(%{}, user.id, AccountView.render("show.json", %{user: user, for: user})) | |||
initial_state = | |||
%{ | |||
@@ -943,49 +517,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def account_register( | |||
%{assigns: %{app: app}} = conn, | |||
%{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params | |||
) do | |||
params = | |||
params | |||
|> Map.take([ | |||
"email", | |||
"captcha_solution", | |||
"captcha_token", | |||
"captcha_answer_data", | |||
"token", | |||
"password" | |||
]) | |||
|> Map.put("nickname", nickname) | |||
|> Map.put("fullname", params["fullname"] || nickname) | |||
|> Map.put("bio", params["bio"] || "") | |||
|> Map.put("confirm", params["password"]) | |||
with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), | |||
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do | |||
json(conn, %{ | |||
token_type: "Bearer", | |||
access_token: token.token, | |||
scope: app.scopes, | |||
created_at: Token.Utils.format_created_at(token) | |||
}) | |||
else | |||
{:error, errors} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(errors) | |||
end | |||
end | |||
def account_register(%{assigns: %{app: _app}} = conn, _) do | |||
render_error(conn, :bad_request, "Missing parameters") | |||
end | |||
def account_register(conn, _) do | |||
render_error(conn, :forbidden, "Invalid credentials") | |||
end | |||
def password_reset(conn, params) do | |||
nickname_or_email = params["email"] || params["nickname"] | |||
@@ -1002,16 +533,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def account_confirmation_resend(conn, params) do | |||
nickname_or_email = params["email"] || params["nickname"] | |||
with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), | |||
{:ok, _} <- User.try_send_confirmation_email(user) do | |||
conn | |||
|> json_response(:no_content, "") | |||
end | |||
end | |||
def try_render(conn, target, params) | |||
when is_binary(target) do | |||
case render(conn, target, params) do | |||
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("accounts.json", users: accounts, for: user, as: :user) | |||
|> render("index.json", users: accounts, for: user, as: :user) | |||
end | |||
def search2(conn, params), do: do_search(:v2, conn, params) | |||
@@ -72,7 +72,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do | |||
defp resource_search(_, "accounts", query, options) do | |||
accounts = with_fallback(fn -> User.search(query, options) end) | |||
AccountView.render("accounts.json", users: accounts, for: options[:for_user], as: :user) | |||
AccountView.render("index.json", users: accounts, for: options[:for_user], as: :user) | |||
end | |||
defp resource_search(_, "statuses", query, options) do | |||
@@ -231,7 +231,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("accounts.json", for: user, users: users, as: :user) | |||
|> render("index.json", for: user, users: users, as: :user) | |||
else | |||
{:visible, false} -> {:error, :not_found} | |||
_ -> json(conn, []) | |||
@@ -251,7 +251,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do | |||
conn | |||
|> put_view(AccountView) | |||
|> render("accounts.json", for: user, users: users, as: :user) | |||
|> render("index.json", for: user, users: users, as: :user) | |||
else | |||
{:visible, false} -> {:error, :not_found} | |||
_ -> json(conn, []) | |||
@@ -11,15 +11,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.MediaProxy | |||
def render("accounts.json", %{users: users} = opts) do | |||
def render("index.json", %{users: users} = opts) do | |||
users | |||
|> render_many(AccountView, "account.json", opts) | |||
|> render_many(AccountView, "show.json", opts) | |||
|> Enum.filter(&Enum.any?/1) | |||
end | |||
def render("account.json", %{user: user} = opts) do | |||
def render("show.json", %{user: user} = opts) do | |||
if User.visible_for?(user, opts[:for]), | |||
do: do_render("account.json", opts), | |||
do: do_render("show.json", opts), | |||
else: %{} | |||
end | |||
@@ -66,7 +66,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do | |||
render_many(targets, AccountView, "relationship.json", user: user, as: :target) | |||
end | |||
defp do_render("account.json", %{user: user} = opts) do | |||
defp do_render("show.json", %{user: user} = opts) do | |||
display_name = HTML.strip_tags(user.name || user.nickname) | |||
image = User.avatar_url(user) |> MediaProxy.url() | |||
@@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do | |||
%{ | |||
id: participation.id |> to_string(), | |||
accounts: render(AccountView, "accounts.json", users: users, as: :user), | |||
accounts: render(AccountView, "index.json", users: users, as: :user), | |||
unread: !participation.read, | |||
last_status: render(StatusView, "show.json", activity: activity, for: user) | |||
} | |||
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do | |||
id: to_string(notification.id), | |||
type: mastodon_type, | |||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at), | |||
account: AccountView.render("account.json", %{user: actor, for: user}), | |||
account: AccountView.render("show.json", %{user: actor, for: user}), | |||
pleroma: %{ | |||
is_seen: notification.seen | |||
} | |||
@@ -108,7 +108,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
id: to_string(activity.id), | |||
uri: activity_object.data["id"], | |||
url: activity_object.data["id"], | |||
account: AccountView.render("account.json", %{user: user, for: opts[:for]}), | |||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}), | |||
in_reply_to_id: nil, | |||
in_reply_to_account_id: nil, | |||
reblog: reblogged, | |||
@@ -258,7 +258,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
id: to_string(activity.id), | |||
uri: object.data["id"], | |||
url: url, | |||
account: AccountView.render("account.json", %{user: user, for: opts[:for]}), | |||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}), | |||
in_reply_to_id: reply_to && to_string(reply_to.id), | |||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id), | |||
reblog: nil, | |||
@@ -376,7 +376,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
%{ | |||
id: activity.id, | |||
account: AccountView.render("account.json", %{user: user, for: opts[:for]}), | |||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}), | |||
created_at: created_at, | |||
title: object.data["title"] |> HTML.strip_tags(), | |||
artist: object.data["artist"] |> HTML.strip_tags(), | |||
@@ -0,0 +1,143 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.PleromaAPI.AccountController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, | |||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] | |||
alias Ecto.Changeset | |||
alias Pleroma.Plugs.RateLimiter | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
require Pleroma.Constants | |||
plug(RateLimiter, :account_confirmation_resend when action == :confirmation_resend) | |||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe]) | |||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView) | |||
@doc "POST /api/v1/pleroma/accounts/confirmation_resend" | |||
def confirmation_resend(conn, params) do | |||
nickname_or_email = params["email"] || params["nickname"] | |||
with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), | |||
{:ok, _} <- User.try_send_confirmation_email(user) do | |||
json_response(conn, :no_content, "") | |||
end | |||
end | |||
@doc "PATCH /api/v1/pleroma/accounts/update_avatar" | |||
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
{:ok, user} = | |||
user | |||
|> Changeset.change(%{avatar: nil}) | |||
|> User.update_and_set_cache() | |||
CommonAPI.update(user) | |||
json(conn, %{url: nil}) | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, params) do | |||
{:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar) | |||
{:ok, user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache() | |||
%{"url" => [%{"href" => href} | _]} = data | |||
CommonAPI.update(user) | |||
json(conn, %{url: href}) | |||
end | |||
@doc "PATCH /api/v1/pleroma/accounts/update_banner" | |||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do | |||
new_info = %{"banner" => %{}} | |||
with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
CommonAPI.update(user) | |||
json(conn, %{url: nil}) | |||
end | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), | |||
new_info <- %{"banner" => object.data}, | |||
{:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
CommonAPI.update(user) | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
json(conn, %{url: href}) | |||
end | |||
end | |||
@doc "PATCH /api/v1/pleroma/accounts/update_background" | |||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do | |||
new_info = %{"background" => %{}} | |||
with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
json(conn, %{url: nil}) | |||
end | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(params, type: :background), | |||
new_info <- %{"background" => object.data}, | |||
{:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do | |||
%{"url" => [%{"href" => href} | _]} = object.data | |||
json(conn, %{url: href}) | |||
end | |||
end | |||
@doc "GET /api/v1/pleroma/accounts/:id/favourites" | |||
def favourites(%{assigns: %{account: %{info: %{hide_favorites: true}}}} = conn, _params) do | |||
render_error(conn, :forbidden, "Can't get favorites") | |||
end | |||
def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do | |||
params = | |||
params | |||
|> Map.put("type", "Create") | |||
|> Map.put("favorited_by", user.ap_id) | |||
|> Map.put("blocking_user", for_user) | |||
recipients = | |||
if for_user do | |||
[Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following] | |||
else | |||
[Pleroma.Constants.as_public()] | |||
end | |||
activities = | |||
recipients | |||
|> ActivityPub.fetch_activities(params) | |||
|> Enum.reverse() | |||
conn | |||
|> add_link_headers(activities) | |||
|> put_view(StatusView) | |||
|> render("index.json", activities: activities, for: for_user, as: :activity) | |||
end | |||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe" | |||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do | |||
with {:ok, subscription_target} <- User.subscribe(user, subscription_target) do | |||
render(conn, "relationship.json", user: user, target: subscription_target) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
@doc "POST /api/v1/pleroma/accounts/:id/unsubscribe" | |||
def unsubscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do | |||
with {:ok, subscription_target} <- User.unsubscribe(user, subscription_target) do | |||
render(conn, "relationship.json", user: user, target: subscription_target) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
end | |||
end | |||
end |
@@ -287,24 +287,40 @@ defmodule Pleroma.Web.Router do | |||
end | |||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do | |||
pipe_through(:authenticated_api) | |||
scope [] do | |||
pipe_through(:authenticated_api) | |||
pipe_through(:oauth_read) | |||
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) | |||
get("/conversations/:id", PleromaAPIController, :conversation) | |||
end | |||
scope [] do | |||
pipe_through(:authenticated_api) | |||
pipe_through(:oauth_write) | |||
patch("/conversations/:id", PleromaAPIController, :update_conversation) | |||
post("/notifications/read", PleromaAPIController, :read_notification) | |||
patch("/accounts/update_avatar", AccountController, :update_avatar) | |||
patch("/accounts/update_banner", AccountController, :update_banner) | |||
patch("/accounts/update_background", AccountController, :update_background) | |||
post("/scrobble", ScrobbleController, :new_scrobble) | |||
end | |||
scope [] do | |||
pipe_through(:oauth_write) | |||
post("/scrobble", ScrobbleController, :new_scrobble) | |||
pipe_through(:api) | |||
pipe_through(:oauth_read_or_public) | |||
get("/accounts/:id/favourites", AccountController, :favourites) | |||
end | |||
scope [] do | |||
pipe_through(:authenticated_api) | |||
pipe_through(:oauth_follow) | |||
post("/accounts/:id/subscribe", AccountController, :subscribe) | |||
post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) | |||
end | |||
post("/accounts/confirmation_resend", AccountController, :confirmation_resend) | |||
end | |||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do | |||
@@ -319,11 +335,11 @@ defmodule Pleroma.Web.Router do | |||
scope [] do | |||
pipe_through(:oauth_read) | |||
get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) | |||
get("/accounts/verify_credentials", AccountController, :verify_credentials) | |||
get("/accounts/relationships", MastodonAPIController, :relationships) | |||
get("/accounts/relationships", AccountController, :relationships) | |||
get("/accounts/:id/lists", MastodonAPIController, :account_lists) | |||
get("/accounts/:id/lists", AccountController, :lists) | |||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) | |||
get("/follow_requests", FollowRequestController, :index) | |||
@@ -364,7 +380,7 @@ defmodule Pleroma.Web.Router do | |||
scope [] do | |||
pipe_through(:oauth_write) | |||
patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) | |||
patch("/accounts/update_credentials", AccountController, :update_credentials) | |||
post("/statuses", StatusController, :create) | |||
delete("/statuses/:id", StatusController, :delete) | |||
@@ -400,10 +416,6 @@ defmodule Pleroma.Web.Router do | |||
put("/filters/:id", FilterController, :update) | |||
delete("/filters/:id", FilterController, :delete) | |||
patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar) | |||
patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner) | |||
patch("/pleroma/accounts/update_background", MastodonAPIController, :update_background) | |||
get("/pleroma/mascot", MastodonAPIController, :get_mascot) | |||
put("/pleroma/mascot", MastodonAPIController, :set_mascot) | |||
@@ -413,23 +425,19 @@ defmodule Pleroma.Web.Router do | |||
scope [] do | |||
pipe_through(:oauth_follow) | |||
post("/follows", MastodonAPIController, :follow) | |||
post("/accounts/:id/follow", MastodonAPIController, :follow) | |||
post("/accounts/:id/unfollow", MastodonAPIController, :unfollow) | |||
post("/accounts/:id/block", MastodonAPIController, :block) | |||
post("/accounts/:id/unblock", MastodonAPIController, :unblock) | |||
post("/accounts/:id/mute", MastodonAPIController, :mute) | |||
post("/accounts/:id/unmute", MastodonAPIController, :unmute) | |||
post("/follows", MastodonAPIController, :follows) | |||
post("/accounts/:id/follow", AccountController, :follow) | |||
post("/accounts/:id/unfollow", AccountController, :unfollow) | |||
post("/accounts/:id/block", AccountController, :block) | |||
post("/accounts/:id/unblock", AccountController, :unblock) | |||
post("/accounts/:id/mute", AccountController, :mute) | |||
post("/accounts/:id/unmute", AccountController, :unmute) | |||
post("/follow_requests/:id/authorize", FollowRequestController, :authorize) | |||
post("/follow_requests/:id/reject", FollowRequestController, :reject) | |||
post("/domain_blocks", DomainBlockController, :create) | |||
delete("/domain_blocks", DomainBlockController, :delete) | |||
post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe) | |||
post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe) | |||
end | |||
scope [] do | |||
@@ -451,7 +459,7 @@ defmodule Pleroma.Web.Router do | |||
scope "/api/v1", Pleroma.Web.MastodonAPI do | |||
pipe_through(:api) | |||
post("/accounts", MastodonAPIController, :account_register) | |||
post("/accounts", AccountController, :create) | |||
get("/instance", MastodonAPIController, :masto_instance) | |||
get("/instance/peers", MastodonAPIController, :peers) | |||
@@ -468,12 +476,6 @@ defmodule Pleroma.Web.Router do | |||
get("/accounts/search", SearchController, :account_search) | |||
post( | |||
"/pleroma/accounts/confirmation_resend", | |||
MastodonAPIController, | |||
:account_confirmation_resend | |||
) | |||
scope [] do | |||
pipe_through(:oauth_read_or_public) | |||
@@ -487,14 +489,12 @@ defmodule Pleroma.Web.Router do | |||
get("/polls/:id", MastodonAPIController, :get_poll) | |||
get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) | |||
get("/accounts/:id/followers", MastodonAPIController, :followers) | |||
get("/accounts/:id/following", MastodonAPIController, :following) | |||
get("/accounts/:id", MastodonAPIController, :user) | |||
get("/accounts/:id/statuses", AccountController, :statuses) | |||
get("/accounts/:id/followers", AccountController, :followers) | |||
get("/accounts/:id/following", AccountController, :following) | |||
get("/accounts/:id", AccountController, :show) | |||
get("/search", SearchController, :search) | |||
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites) | |||
end | |||
end | |||
@@ -113,10 +113,10 @@ defmodule Pleroma.ListTest do | |||
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1) | |||
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_2) | |||
lists_1 = Pleroma.List.get_lists_account_belongs(owner, member_1.id) | |||
lists_1 = Pleroma.List.get_lists_account_belongs(owner, member_1) | |||
assert owned_list in lists_1 | |||
refute not_owned_list in lists_1 | |||
lists_2 = Pleroma.List.get_lists_account_belongs(owner, member_2.id) | |||
lists_2 = Pleroma.List.get_lists_account_belongs(owner, member_2) | |||
assert owned_list in lists_2 | |||
refute not_owned_list in lists_2 | |||
end | |||
@@ -1231,7 +1231,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do | |||
{:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) | |||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) | |||
{:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) | |||
end | |||
end | |||
@@ -21,12 +21,12 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do | |||
content: nil, | |||
actor: | |||
Map.merge( | |||
AccountView.render("account.json", %{user: user}), | |||
AccountView.render("show.json", %{user: user}), | |||
Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}) | |||
), | |||
account: | |||
Map.merge( | |||
AccountView.render("account.json", %{user: other_user}), | |||
AccountView.render("show.json", %{user: other_user}), | |||
Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user}) | |||
), | |||
statuses: [], | |||
@@ -53,12 +53,12 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do | |||
content: nil, | |||
actor: | |||
Map.merge( | |||
AccountView.render("account.json", %{user: user}), | |||
AccountView.render("show.json", %{user: user}), | |||
Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}) | |||
), | |||
account: | |||
Map.merge( | |||
AccountView.render("account.json", %{user: other_user}), | |||
AccountView.render("show.json", %{user: other_user}), | |||
Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user}) | |||
), | |||
statuses: [StatusView.render("show.json", %{activity: activity})], | |||
@@ -0,0 +1,852 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.OAuth.Token | |||
import Pleroma.Factory | |||
describe "account fetching" do | |||
test "works by id" do | |||
user = insert(:user) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.id}") | |||
assert %{"id" => id} = json_response(conn, 200) | |||
assert id == to_string(user.id) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/-1") | |||
assert %{"error" => "Can't find user"} = json_response(conn, 404) | |||
end | |||
test "works by nickname" do | |||
user = insert(:user) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.nickname}") | |||
assert %{"id" => id} = json_response(conn, 200) | |||
assert id == user.id | |||
end | |||
test "works by nickname for remote users" do | |||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) | |||
Pleroma.Config.put([:instance, :limit_to_local_content], false) | |||
user = insert(:user, nickname: "user@example.com", local: false) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.nickname}") | |||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) | |||
assert %{"id" => id} = json_response(conn, 200) | |||
assert id == user.id | |||
end | |||
test "respects limit_to_local_content == :all for remote user nicknames" do | |||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) | |||
Pleroma.Config.put([:instance, :limit_to_local_content], :all) | |||
user = insert(:user, nickname: "user@example.com", local: false) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.nickname}") | |||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) | |||
assert json_response(conn, 404) | |||
end | |||
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do | |||
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) | |||
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) | |||
user = insert(:user, nickname: "user@example.com", local: false) | |||
reading_user = insert(:user) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.nickname}") | |||
assert json_response(conn, 404) | |||
conn = | |||
build_conn() | |||
|> assign(:user, reading_user) | |||
|> get("/api/v1/accounts/#{user.nickname}") | |||
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) | |||
assert %{"id" => id} = json_response(conn, 200) | |||
assert id == user.id | |||
end | |||
test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do | |||
# Need to set an old-style integer ID to reproduce the problem | |||
# (these are no longer assigned to new accounts but were preserved | |||
# for existing accounts during the migration to flakeIDs) | |||
user_one = insert(:user, %{id: 1212}) | |||
user_two = insert(:user, %{nickname: "#{user_one.id}garbage"}) | |||
resp_one = | |||
conn | |||
|> get("/api/v1/accounts/#{user_one.id}") | |||
resp_two = | |||
conn | |||
|> get("/api/v1/accounts/#{user_two.nickname}") | |||
resp_three = | |||
conn | |||
|> get("/api/v1/accounts/#{user_two.id}") | |||
acc_one = json_response(resp_one, 200) | |||
acc_two = json_response(resp_two, 200) | |||
acc_three = json_response(resp_three, 200) | |||
refute acc_one == acc_two | |||
assert acc_two == acc_three | |||
end | |||
end | |||
describe "user timelines" do | |||
test "gets a users statuses", %{conn: conn} do | |||
user_one = insert(:user) | |||
user_two = insert(:user) | |||
user_three = insert(:user) | |||
{:ok, user_three} = User.follow(user_three, user_one) | |||
{:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) | |||
{:ok, direct_activity} = | |||
CommonAPI.post(user_one, %{ | |||
"status" => "Hi, @#{user_two.nickname}.", | |||
"visibility" => "direct" | |||
}) | |||
{:ok, private_activity} = | |||
CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) | |||
resp = | |||
conn | |||
|> get("/api/v1/accounts/#{user_one.id}/statuses") | |||
assert [%{"id" => id}] = json_response(resp, 200) | |||
assert id == to_string(activity.id) | |||
resp = | |||
conn | |||
|> assign(:user, user_two) | |||
|> get("/api/v1/accounts/#{user_one.id}/statuses") | |||
assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) | |||
assert id_one == to_string(direct_activity.id) | |||
assert id_two == to_string(activity.id) | |||
resp = | |||
conn | |||
|> assign(:user, user_three) | |||
|> get("/api/v1/accounts/#{user_one.id}/statuses") | |||
assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) | |||
assert id_one == to_string(private_activity.id) | |||
assert id_two == to_string(activity.id) | |||
end | |||
test "unimplemented pinned statuses feature", %{conn: conn} do | |||
note = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note.data["actor"]) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") | |||
assert json_response(conn, 200) == [] | |||
end | |||
test "gets an users media", %{conn: conn} do | |||
note = insert(:note_activity) | |||
user = User.get_cached_by_ap_id(note.data["actor"]) | |||
file = %Plug.Upload{ | |||
content_type: "image/jpg", | |||
path: Path.absname("test/fixtures/image.jpg"), | |||
filename: "an_image.jpg" | |||
} | |||
{:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) | |||
{:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(image_post.id) | |||
conn = | |||
build_conn() | |||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(image_post.id) | |||
end | |||
test "gets a user's statuses without reblogs", %{conn: conn} do | |||
user = insert(:user) | |||
{:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) | |||
{:ok, _, _} = CommonAPI.repeat(post.id, user) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(post.id) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(post.id) | |||
end | |||
test "filters user's statuses by a hashtag", %{conn: conn} do | |||
user = insert(:user) | |||
{:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) | |||
{:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(post.id) | |||
end | |||
end | |||
describe "followers" do | |||
test "getting followers", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{other_user.id}/followers") | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(user.id) | |||
end | |||
test "getting followers, hide_followers", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user, %{info: %{hide_followers: true}}) | |||
{:ok, _user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{other_user.id}/followers") | |||
assert [] == json_response(conn, 200) | |||
end | |||
test "getting followers, hide_followers, same user requesting", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user, %{info: %{hide_followers: true}}) | |||
{:ok, _user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> assign(:user, other_user) | |||
|> get("/api/v1/accounts/#{other_user.id}/followers") | |||
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/min_id=#{follower2.id}/ | |||
assert link_header =~ ~r/max_id=#{follower2.id}/ | |||
end | |||
end | |||
describe "following" do | |||
test "getting following", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/following") | |||
assert [%{"id" => id}] = json_response(conn, 200) | |||
assert id == to_string(other_user.id) | |||
end | |||
test "getting following, hide_follows", %{conn: conn} do | |||
user = insert(:user, %{info: %{hide_follows: true}}) | |||
other_user = insert(:user) | |||
{:ok, user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> get("/api/v1/accounts/#{user.id}/following") | |||
assert [] == json_response(conn, 200) | |||
end | |||
test "getting following, hide_follows, same user requesting", %{conn: conn} do | |||
user = insert(:user, %{info: %{hide_follows: true}}) | |||
other_user = insert(:user) | |||
{:ok, user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/#{user.id}/following") | |||
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/min_id=#{following2.id}/ | |||
assert link_header =~ ~r/max_id=#{following2.id}/ | |||
end | |||
end | |||
describe "follow/unfollow" do | |||
test "following / unfollowing a user", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/follow") | |||
assert %{"id" => _id, "following" => true} = json_response(conn, 200) | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/unfollow") | |||
assert %{"id" => _id, "following" => false} = json_response(conn, 200) | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/follows", %{"uri" => other_user.nickname}) | |||
assert %{"id" => id} = json_response(conn, 200) | |||
assert id == to_string(other_user.id) | |||
end | |||
test "following without reblogs" do | |||
follower = insert(:user) | |||
followed = insert(:user) | |||
other_user = insert(:user) | |||
conn = | |||
build_conn() | |||
|> assign(:user, follower) | |||
|> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false") | |||
assert %{"showing_reblogs" => false} = json_response(conn, 200) | |||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) | |||
{:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) | |||
conn = | |||
build_conn() | |||
|> assign(:user, User.get_cached_by_id(follower.id)) | |||
|> get("/api/v1/timelines/home") | |||
assert [] == json_response(conn, 200) | |||
conn = | |||
build_conn() | |||
|> assign(:user, follower) | |||
|> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") | |||
assert %{"showing_reblogs" => true} = json_response(conn, 200) | |||
conn = | |||
build_conn() | |||
|> assign(:user, User.get_cached_by_id(follower.id)) | |||
|> get("/api/v1/timelines/home") | |||
expected_activity_id = reblog.id | |||
assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) | |||
end | |||
test "following / unfollowing errors" do | |||
user = insert(:user) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
# self follow | |||
conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
# self unfollow | |||
user = User.get_cached_by_id(user.id) | |||
conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow") | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
# self follow via uri | |||
user = User.get_cached_by_id(user.id) | |||
conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname}) | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
# follow non existing user | |||
conn_res = post(conn, "/api/v1/accounts/doesntexist/follow") | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
# follow non existing user via uri | |||
conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"}) | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
# unfollow non existing user | |||
conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow") | |||
assert %{"error" => "Record not found"} = json_response(conn_res, 404) | |||
end | |||
end | |||
describe "mute/unmute" do | |||
test "with notifications", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/mute") | |||
response = json_response(conn, 200) | |||
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/unmute") | |||
response = json_response(conn, 200) | |||
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response | |||
end | |||
test "without notifications", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) | |||
response = json_response(conn, 200) | |||
assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/unmute") | |||
response = json_response(conn, 200) | |||
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response | |||
end | |||
end | |||
describe "pinned statuses" do | |||
setup do | |||
user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) | |||
[user: user, activity: activity] | |||
end | |||
test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do | |||
{:ok, _} = CommonAPI.pin(activity.id, user) | |||
result = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") | |||
|> json_response(200) | |||
id_str = to_string(activity.id) | |||
assert [%{"id" => ^id_str, "pinned" => true}] = result | |||
end | |||
end | |||
test "blocking / unblocking a user", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/block") | |||
assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) | |||
user = User.get_cached_by_id(user.id) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/accounts/#{other_user.id}/unblock") | |||
assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) | |||
end | |||
describe "create account by app" do | |||
setup do | |||
valid_params = %{ | |||
username: "lain", | |||
email: "lain@example.org", | |||
password: "PlzDontHackLain", | |||
agreement: true | |||
} | |||
[valid_params: valid_params] | |||
end | |||
test "Account registration via Application", %{conn: conn} do | |||
conn = | |||
conn | |||
|> post("/api/v1/apps", %{ | |||
client_name: "client_name", | |||
redirect_uris: "urn:ietf:wg:oauth:2.0:oob", | |||
scopes: "read, write, follow" | |||
}) | |||
%{ | |||
"client_id" => client_id, | |||
"client_secret" => client_secret, | |||
"id" => _, | |||
"name" => "client_name", | |||
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", | |||
"vapid_key" => _, | |||
"website" => nil | |||
} = json_response(conn, 200) | |||
conn = | |||
conn | |||
|> post("/oauth/token", %{ | |||
grant_type: "client_credentials", | |||
client_id: client_id, | |||
client_secret: client_secret | |||
}) | |||
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} = | |||
json_response(conn, 200) | |||
assert token | |||
token_from_db = Repo.get_by(Token, token: token) | |||
assert token_from_db | |||
assert refresh | |||
assert scope == "read write follow" | |||
conn = | |||
build_conn() | |||
|> put_req_header("authorization", "Bearer " <> token) | |||
|> post("/api/v1/accounts", %{ | |||
username: "lain", | |||
email: "lain@example.org", | |||
password: "PlzDontHackLain", | |||
bio: "Test Bio", | |||
agreement: true | |||
}) | |||
%{ | |||
"access_token" => token, | |||
"created_at" => _created_at, | |||
"scope" => _scope, | |||
"token_type" => "Bearer" | |||
} = json_response(conn, 200) | |||
token_from_db = Repo.get_by(Token, token: token) | |||
assert token_from_db | |||
token_from_db = Repo.preload(token_from_db, :user) | |||
assert token_from_db.user | |||
assert token_from_db.user.info.confirmation_pending | |||
end | |||
test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do | |||
_user = insert(:user, email: "lain@example.org") | |||
app_token = insert(:oauth_token, user: nil) | |||
conn = | |||
conn | |||
|> put_req_header("authorization", "Bearer " <> app_token.token) | |||
res = post(conn, "/api/v1/accounts", valid_params) | |||
assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"} | |||
end | |||
test "rate limit", %{conn: conn} do | |||
app_token = insert(:oauth_token, user: nil) | |||
conn = | |||
put_req_header(conn, "authorization", "Bearer " <> app_token.token) | |||
|> Map.put(:remote_ip, {15, 15, 15, 15}) | |||
for i <- 1..5 do | |||
conn = | |||
conn | |||
|> post("/api/v1/accounts", %{ | |||
username: "#{i}lain", | |||
email: "#{i}lain@example.org", | |||
password: "PlzDontHackLain", | |||
agreement: true | |||
}) | |||
%{ | |||
"access_token" => token, | |||
"created_at" => _created_at, | |||
"scope" => _scope, | |||
"token_type" => "Bearer" | |||
} = json_response(conn, 200) | |||
token_from_db = Repo.get_by(Token, token: token) | |||
assert token_from_db | |||
token_from_db = Repo.preload(token_from_db, :user) | |||
assert token_from_db.user | |||
assert token_from_db.user.info.confirmation_pending | |||
end | |||
conn = | |||
conn | |||
|> post("/api/v1/accounts", %{ | |||
username: "6lain", | |||
email: "6lain@example.org", | |||
password: "PlzDontHackLain", | |||
agreement: true | |||
}) | |||
assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"} | |||
end | |||
test "returns bad_request if missing required params", %{ | |||
conn: conn, | |||
valid_params: valid_params | |||
} do | |||
app_token = insert(:oauth_token, user: nil) | |||
conn = | |||
conn | |||
|> put_req_header("authorization", "Bearer " <> app_token.token) | |||
res = post(conn, "/api/v1/accounts", valid_params) | |||
assert json_response(res, 200) | |||
[{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}] | |||
|> Stream.zip(valid_params) | |||
|> Enum.each(fn {ip, {attr, _}} -> | |||
res = | |||
conn | |||
|> Map.put(:remote_ip, ip) | |||
|> post("/api/v1/accounts", Map.delete(valid_params, attr)) | |||
|> json_response(400) | |||
assert res == %{"error" => "Missing parameters"} | |||
end) | |||
end | |||
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do | |||
conn = | |||
conn | |||
|> put_req_header("authorization", "Bearer " <> "invalid-token") | |||
res = post(conn, "/api/v1/accounts", valid_params) | |||
assert json_response(res, 403) == %{"error" => "Invalid credentials"} | |||
end | |||
end | |||
describe "GET /api/v1/accounts/:id/lists - account_lists" do | |||
test "returns lists to which the account belongs", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user) | |||
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) | |||
res = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/#{other_user.id}/lists") | |||
|> json_response(200) | |||
assert res == [%{"id" => to_string(list.id), "title" => "Test List"}] | |||
end | |||
end | |||
describe "verify_credentials" do | |||
test "verify_credentials", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/verify_credentials") | |||
response = json_response(conn, 200) | |||
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response | |||
assert response["pleroma"]["chat_token"] | |||
assert id == to_string(user.id) | |||
end | |||
test "verify_credentials default scope unlisted", %{conn: conn} do | |||
user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/verify_credentials") | |||
assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200) | |||
assert id == to_string(user.id) | |||
end | |||
test "locked accounts", %{conn: conn} do | |||
user = insert(:user, %{info: %User.Info{default_scope: "private"}}) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/verify_credentials") | |||
assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) | |||
assert id == to_string(user.id) | |||
end | |||
end | |||
describe "user relationships" do | |||
test "returns the relationships for the current user", %{conn: conn} do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
{:ok, user} = User.follow(user, other_user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) | |||
assert [relationship] = json_response(conn, 200) | |||
assert to_string(other_user.id) == relationship["id"] | |||
end | |||
test "returns an empty list on a bad request", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/accounts/relationships", %{}) | |||
assert [] = json_response(conn, 200) | |||
end | |||
end | |||
end |
@@ -3,7 +3,7 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do | |||
use Pleroma.Web.ConnCase, async: true | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.User | |||
@@ -88,7 +88,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
} | |||
} | |||
assert expected == AccountView.render("account.json", %{user: user}) | |||
assert expected == AccountView.render("show.json", %{user: user}) | |||
end | |||
test "Represent the user account for the account owner" do | |||
@@ -106,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
assert %{ | |||
pleroma: %{notification_settings: ^notification_settings}, | |||
source: %{privacy: ^privacy} | |||
} = AccountView.render("account.json", %{user: user, for: user}) | |||
} = AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
test "Represent a Service(bot) account" do | |||
@@ -160,13 +160,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
} | |||
} | |||
assert expected == AccountView.render("account.json", %{user: user}) | |||
assert expected == AccountView.render("show.json", %{user: user}) | |||
end | |||
test "Represent a deactivated user for an admin" do | |||
admin = insert(:user, %{info: %{is_admin: true}}) | |||
deactivated_user = insert(:user, %{info: %{deactivated: true}}) | |||
represented = AccountView.render("account.json", %{user: deactivated_user, for: admin}) | |||
represented = AccountView.render("show.json", %{user: deactivated_user, for: admin}) | |||
assert represented[:pleroma][:deactivated] == true | |||
end | |||
@@ -348,27 +348,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
} | |||
} | |||
assert expected == AccountView.render("account.json", %{user: user, for: other_user}) | |||
assert expected == AccountView.render("show.json", %{user: user, for: other_user}) | |||
end | |||
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do | |||
user = insert(:user, %{info: %User.Info{pleroma_settings_store: %{fe: "test"}}}) | |||
result = | |||
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true}) | |||
AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true}) | |||
assert result.pleroma.settings_store == %{:fe => "test"} | |||
result = AccountView.render("account.json", %{user: user, with_pleroma_settings: true}) | |||
result = AccountView.render("show.json", %{user: user, with_pleroma_settings: true}) | |||
assert result.pleroma[:settings_store] == nil | |||
result = AccountView.render("account.json", %{user: user, for: user}) | |||
result = AccountView.render("show.json", %{user: user, for: user}) | |||
assert result.pleroma[:settings_store] == nil | |||
end | |||
test "sanitizes display names" do | |||
user = insert(:user, name: "<marquee> username </marquee>") | |||
result = AccountView.render("account.json", %{user: user}) | |||
result = AccountView.render("show.json", %{user: user}) | |||
refute result.display_name == "<marquee> username </marquee>" | |||
end | |||
@@ -391,7 +391,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
followers_count: 0, | |||
following_count: 0, | |||
pleroma: %{hide_follows_count: true, hide_followers_count: true} | |||
} = AccountView.render("account.json", %{user: user}) | |||
} = AccountView.render("show.json", %{user: user}) | |||
end | |||
test "shows when follows/followers are hidden" do | |||
@@ -404,7 +404,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
followers_count: 1, | |||
following_count: 1, | |||
pleroma: %{hide_follows: true, hide_followers: true} | |||
} = AccountView.render("account.json", %{user: user}) | |||
} = AccountView.render("show.json", %{user: user}) | |||
end | |||
test "shows actual follower/following count to the account owner" do | |||
@@ -416,7 +416,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
assert %{ | |||
followers_count: 1, | |||
following_count: 1 | |||
} = AccountView.render("account.json", %{user: user, for: user}) | |||
} = AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
end | |||
@@ -425,65 +425,65 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
user = insert(:user) | |||
assert %{follow_requests_count: 0} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
other_user = insert(:user) | |||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{follow_requests_count: 0} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
test "shows non-zero when follow requests are pending" do | |||
user = insert(:user, %{info: %{locked: true}}) | |||
assert %{locked: true} = AccountView.render("account.json", %{user: user, for: user}) | |||
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) | |||
other_user = insert(:user) | |||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{locked: true, follow_requests_count: 1} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
test "decreases when accepting a follow request" do | |||
user = insert(:user, %{info: %{locked: true}}) | |||
assert %{locked: true} = AccountView.render("account.json", %{user: user, for: user}) | |||
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) | |||
other_user = insert(:user) | |||
{:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{locked: true, follow_requests_count: 1} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
{:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) | |||
assert %{locked: true, follow_requests_count: 0} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
test "decreases when rejecting a follow request" do | |||
user = insert(:user, %{info: %{locked: true}}) | |||
assert %{locked: true} = AccountView.render("account.json", %{user: user, for: user}) | |||
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) | |||
other_user = insert(:user) | |||
{:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
assert %{locked: true, follow_requests_count: 1} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
{:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) | |||
assert %{locked: true, follow_requests_count: 0} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
test "shows non-zero when historical unapproved requests are present" do | |||
user = insert(:user, %{info: %{locked: true}}) | |||
assert %{locked: true} = AccountView.render("account.json", %{user: user, for: user}) | |||
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) | |||
other_user = insert(:user) | |||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) | |||
@@ -491,7 +491,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do | |||
{:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: false})) | |||
assert %{locked: false, follow_requests_count: 1} = | |||
AccountView.render("account.json", %{user: user, for: user}) | |||
AccountView.render("show.json", %{user: user, for: user}) | |||
end | |||
end | |||
end |
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
id: to_string(notification.id), | |||
pleroma: %{is_seen: false}, | |||
type: "mention", | |||
account: AccountView.render("account.json", %{user: user, for: mentioned_user}), | |||
account: AccountView.render("show.json", %{user: user, for: mentioned_user}), | |||
status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}), | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
@@ -50,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
id: to_string(notification.id), | |||
pleroma: %{is_seen: false}, | |||
type: "favourite", | |||
account: AccountView.render("account.json", %{user: another_user, for: user}), | |||
account: AccountView.render("show.json", %{user: another_user, for: user}), | |||
status: StatusView.render("show.json", %{activity: create_activity, for: user}), | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
@@ -72,7 +72,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
id: to_string(notification.id), | |||
pleroma: %{is_seen: false}, | |||
type: "reblog", | |||
account: AccountView.render("account.json", %{user: another_user, for: user}), | |||
account: AccountView.render("show.json", %{user: another_user, for: user}), | |||
status: StatusView.render("show.json", %{activity: reblog_activity, for: user}), | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
@@ -92,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do | |||
id: to_string(notification.id), | |||
pleroma: %{is_seen: false}, | |||
type: "follow", | |||
account: AccountView.render("account.json", %{user: follower, for: followed}), | |||
account: AccountView.render("show.json", %{user: follower, for: followed}), | |||
created_at: Utils.to_masto_date(notification.inserted_at) | |||
} | |||
@@ -103,7 +103,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
id: to_string(note.id), | |||
uri: object_data["id"], | |||
url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note), | |||
account: AccountView.render("account.json", %{user: user}), | |||
account: AccountView.render("show.json", %{user: user}), | |||
in_reply_to_id: nil, | |||
in_reply_to_account_id: nil, | |||
card: nil, | |||
@@ -0,0 +1,395 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.Config | |||
alias Pleroma.Repo | |||
alias Pleroma.Tests.ObanHelpers | |||
alias Pleroma.User | |||
alias Pleroma.Web.CommonAPI | |||
import Pleroma.Factory | |||
import Swoosh.TestAssertions | |||
@image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" | |||
describe "POST /api/v1/pleroma/accounts/confirmation_resend" do | |||
setup do | |||
{:ok, user} = | |||
insert(:user) | |||
|> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true)) | |||
|> Repo.update() | |||
assert user.info.confirmation_pending | |||
[user: user] | |||
end | |||
clear_config([:instance, :account_activation_required]) do | |||
Config.put([:instance, :account_activation_required], true) | |||
end | |||
test "resend account confirmation email", %{conn: conn, user: user} do | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") | |||
|> json_response(:no_content) | |||
ObanHelpers.perform_all() | |||
email = Pleroma.Emails.UserEmail.account_confirmation_email(user) | |||
notify_email = Config.get([:instance, :notify_email]) | |||
instance_name = Config.get([:instance, :name]) | |||
assert_email_sent( | |||
from: {instance_name, notify_email}, | |||
to: {user.name, user.email}, | |||
html_body: email.html_body | |||
) | |||
end | |||
end | |||
describe "PATCH /api/v1/pleroma/accounts/update_avatar" do | |||
test "user avatar can be set", %{conn: conn} do | |||
user = insert(:user) | |||
avatar_image = File.read!("test/fixtures/avatar_data_uri") | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) | |||
user = refresh_record(user) | |||
assert %{ | |||
"name" => _, | |||
"type" => _, | |||
"url" => [ | |||
%{ | |||
"href" => _, | |||
"mediaType" => _, | |||
"type" => _ | |||
} | |||
] | |||
} = user.avatar | |||
assert %{"url" => _} = json_response(conn, 200) | |||
end | |||
test "user avatar can be reset", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) | |||
user = User.get_cached_by_id(user.id) | |||
assert user.avatar == nil | |||
assert %{"url" => nil} = json_response(conn, 200) | |||
end | |||
end | |||
describe "PATCH /api/v1/pleroma/accounts/update_banner" do | |||
test "can set profile banner", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) | |||
user = refresh_record(user) | |||
assert user.info.banner["type"] == "Image" | |||
assert %{"url" => _} = json_response(conn, 200) | |||
end | |||
test "can reset profile banner", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) | |||
user = refresh_record(user) | |||
assert user.info.banner == %{} | |||
assert %{"url" => nil} = json_response(conn, 200) | |||
end | |||
end | |||
describe "PATCH /api/v1/pleroma/accounts/update_background" do | |||
test "background image can be set", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) | |||
user = refresh_record(user) | |||
assert user.info.background["type"] == "Image" | |||
assert %{"url" => _} = json_response(conn, 200) | |||
end | |||
test "background image can be reset", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) | |||
user = refresh_record(user) | |||
assert user.info.background == %{} | |||
assert %{"url" => nil} = json_response(conn, 200) | |||
end | |||
end | |||
describe "getting favorites timeline of specified user" do | |||
setup do | |||
[current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}}) | |||
[current_user: current_user, user: user] | |||
end | |||
test "returns list of statuses favorited by specified user", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
[activity | _] = insert_pair(:note_activity) | |||
CommonAPI.favorite(activity.id, user) | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
[like] = response | |||
assert length(response) == 1 | |||
assert like["id"] == activity.id | |||
end | |||
test "returns favorites for specified user_id when user is not logged in", %{ | |||
conn: conn, | |||
user: user | |||
} do | |||
activity = insert(:note_activity) | |||
CommonAPI.favorite(activity.id, user) | |||
response = | |||
conn | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
assert length(response) == 1 | |||
end | |||
test "returns favorited DM only when user is logged in and he is one of recipients", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
{:ok, direct} = | |||
CommonAPI.post(current_user, %{ | |||
"status" => "Hi @#{user.nickname}!", | |||
"visibility" => "direct" | |||
}) | |||
CommonAPI.favorite(direct.id, user) | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
assert length(response) == 1 | |||
anonymous_response = | |||
conn | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
assert Enum.empty?(anonymous_response) | |||
end | |||
test "does not return others' favorited DM when user is not one of recipients", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
user_two = insert(:user) | |||
{:ok, direct} = | |||
CommonAPI.post(user_two, %{ | |||
"status" => "Hi @#{user.nickname}!", | |||
"visibility" => "direct" | |||
}) | |||
CommonAPI.favorite(direct.id, user) | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
assert Enum.empty?(response) | |||
end | |||
test "paginates favorites using since_id and max_id", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
activities = insert_list(10, :note_activity) | |||
Enum.each(activities, fn activity -> | |||
CommonAPI.favorite(activity.id, user) | |||
end) | |||
third_activity = Enum.at(activities, 2) | |||
seventh_activity = Enum.at(activities, 6) | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{ | |||
since_id: third_activity.id, | |||
max_id: seventh_activity.id | |||
}) | |||
|> json_response(:ok) | |||
assert length(response) == 3 | |||
refute third_activity in response | |||
refute seventh_activity in response | |||
end | |||
test "limits favorites using limit parameter", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
7 | |||
|> insert_list(:note_activity) | |||
|> Enum.each(fn activity -> | |||
CommonAPI.favorite(activity.id, user) | |||
end) | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"}) | |||
|> json_response(:ok) | |||
assert length(response) == 3 | |||
end | |||
test "returns empty response when user does not have any favorited statuses", %{ | |||
conn: conn, | |||
current_user: current_user, | |||
user: user | |||
} do | |||
response = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
|> json_response(:ok) | |||
assert Enum.empty?(response) | |||
end | |||
test "returns 404 error when specified user is not exist", %{conn: conn} do | |||
conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") | |||
assert json_response(conn, 404) == %{"error" => "Record not found"} | |||
end | |||
test "returns 403 error when user has hidden own favorites", %{ | |||
conn: conn, | |||
current_user: current_user | |||
} do | |||
user = insert(:user, %{info: %{hide_favorites: true}}) | |||
activity = insert(:note_activity) | |||
CommonAPI.favorite(activity.id, user) | |||
conn = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
assert json_response(conn, 403) == %{"error" => "Can't get favorites"} | |||
end | |||
test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do | |||
user = insert(:user) | |||
activity = insert(:note_activity) | |||
CommonAPI.favorite(activity.id, user) | |||
conn = | |||
conn | |||
|> assign(:user, current_user) | |||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") | |||
assert user.info.hide_favorites | |||
assert json_response(conn, 403) == %{"error" => "Can't get favorites"} | |||
end | |||
end | |||
describe "subscribing / unsubscribing" do | |||
test "subscribing / unsubscribing to a user", %{conn: conn} do | |||
user = insert(:user) | |||
subscription_target = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") | |||
assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200) | |||
conn = | |||
build_conn() | |||
|> assign(:user, user) | |||
|> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") | |||
assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200) | |||
end | |||
end | |||
describe "subscribing" do | |||
test "returns 404 when subscription_target not found", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/pleroma/accounts/target_id/subscribe") | |||
assert %{"error" => "Record not found"} = json_response(conn, 404) | |||
end | |||
end | |||
describe "unsubscribing" do | |||
test "returns 404 when subscription_target not found", %{conn: conn} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
|> post("/api/v1/pleroma/accounts/target_id/unsubscribe") | |||
assert %{"error" => "Record not found"} = json_response(conn, 404) | |||
end | |||
end | |||
end |
@@ -1,3 +1,7 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do | |||
use Pleroma.Web.ConnCase | |||
@@ -29,8 +29,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
fetched_user = User.get_cached_by_nickname("lain") | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
end | |||
test "it registers a new user with empty string in bio and returns the user." do | |||
@@ -47,8 +47,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
fetched_user = User.get_cached_by_nickname("lain") | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
end | |||
test "it sends confirmation email if :account_activation_required is specified in instance config" do | |||
@@ -148,8 +148,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
assert invite.used == true | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
end | |||
test "returns error on invalid token" do | |||
@@ -213,8 +213,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
{:ok, user} = TwitterAPI.register_user(data) | |||
fetched_user = User.get_cached_by_nickname("vinny") | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
end | |||
{:ok, data: data, check_fn: check_fn} | |||
@@ -288,8 +288,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
assert invite.used == true | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
data = %{ | |||
"nickname" => "GrimReaper", | |||
@@ -339,8 +339,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
refute invite.used | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
end | |||
test "error after max uses" do | |||
@@ -363,8 +363,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do | |||
invite = Repo.get_by(UserInviteToken, token: invite.token) | |||
assert invite.used == true | |||
assert AccountView.render("account.json", %{user: user}) == | |||
AccountView.render("account.json", %{user: fetched_user}) | |||
assert AccountView.render("show.json", %{user: user}) == | |||
AccountView.render("show.json", %{user: fetched_user}) | |||
data = %{ | |||
"nickname" => "GrimReaper", | |||