Merge branch 'restricted-relations-embedding' into 'develop'

Restricted support for embedded relationships

See merge request pleroma/pleroma!2456
This commit is contained in:
lain 2020-05-17 07:26:51 +00:00
commit 4b28d812cf
31 changed files with 193 additions and 323 deletions

View file

@ -387,56 +387,47 @@ defp render_timelines(user) do
favourites = ActivityPub.fetch_favourites(user) favourites = ActivityPub.fetch_favourites(user)
output_relationships =
!!Pleroma.Config.get([:extensions, :output_relationships_in_statuses_by_default])
Benchee.run( Benchee.run(
%{ %{
"Rendering home timeline" => fn -> "Rendering home timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: home_activities, activities: home_activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: !output_relationships
}) })
end, end,
"Rendering direct timeline" => fn -> "Rendering direct timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: direct_activities, activities: direct_activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: !output_relationships
}) })
end, end,
"Rendering public timeline" => fn -> "Rendering public timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: public_activities, activities: public_activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: !output_relationships
}) })
end, end,
"Rendering tag timeline" => fn -> "Rendering tag timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: tag_activities, activities: tag_activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: !output_relationships
}) })
end, end,
"Rendering notifications" => fn -> "Rendering notifications" => fn ->
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
notifications: notifications, notifications: notifications,
for: user, for: user
skip_relationships: !output_relationships
}) })
end, end,
"Rendering favourites timeline" => fn -> "Rendering favourites timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: favourites, activities: favourites,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: !output_relationships
}) })
end end
}, },

View file

@ -251,8 +251,6 @@
] ]
] ]
config :pleroma, :extensions, output_relationships_in_statuses_by_default: true
config :pleroma, :feed, config :pleroma, :feed,
post_title: %{ post_title: %{
max_length: 100, max_length: 100,

View file

@ -67,8 +67,7 @@ def run(["render_timeline", nickname | _] = args) do
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: true
}) })
end end
}, },

View file

@ -87,6 +87,22 @@ def dictionary(
source_to_target_rel_types \\ nil, source_to_target_rel_types \\ nil,
target_to_source_rel_types \\ nil target_to_source_rel_types \\ nil
) )
def dictionary(
_source_users,
_target_users,
[] = _source_to_target_rel_types,
[] = _target_to_source_rel_types
) do
[]
end
def dictionary(
source_users,
target_users,
source_to_target_rel_types,
target_to_source_rel_types
)
when is_list(source_users) and is_list(target_users) do when is_list(source_users) and is_list(target_users) do
source_user_ids = User.binary_id(source_users) source_user_ids = User.binary_id(source_users)
target_user_ids = User.binary_id(target_users) target_user_ids = User.binary_id(target_users)
@ -138,11 +154,16 @@ def view_relationships_option(nil = _reading_user, _actors, _opts) do
def view_relationships_option(%User{} = reading_user, actors, opts) do def view_relationships_option(%User{} = reading_user, actors, opts) do
{source_to_target_rel_types, target_to_source_rel_types} = {source_to_target_rel_types, target_to_source_rel_types} =
if opts[:source_mutes_only] do case opts[:subset] do
# This option is used for rendering statuses (FE needs `muted` flag for each one anyways) :source_mutes ->
{[:mute], []} # Used for statuses rendering (FE needs `muted` flag for each status when statuses load)
else {[:mute], []}
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
nil ->
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
unknown ->
raise "Unsupported :subset option value: #{inspect(unknown)}"
end end
user_relationships = user_relationships =
@ -153,7 +174,17 @@ def view_relationships_option(%User{} = reading_user, actors, opts) do
target_to_source_rel_types target_to_source_rel_types
) )
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) following_relationships =
case opts[:subset] do
:source_mutes ->
[]
nil ->
FollowingRelationship.all_between_user_sets([reading_user], actors)
unknown ->
raise "Unsupported :subset option value: #{inspect(unknown)}"
end
%{user_relationships: user_relationships, following_relationships: following_relationships} %{user_relationships: user_relationships, following_relationships: following_relationships}
end end

View file

@ -22,6 +22,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.AdminAPI.ConfigView alias Pleroma.Web.AdminAPI.ConfigView
alias Pleroma.Web.AdminAPI.ModerationLogView alias Pleroma.Web.AdminAPI.ModerationLogView
@ -30,8 +31,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
alias Pleroma.Web.MastodonAPI
alias Pleroma.Web.MastodonAPI.AppView alias Pleroma.Web.MastodonAPI.AppView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.App
alias Pleroma.Web.Router alias Pleroma.Web.Router
@ -280,8 +281,8 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
}) })
conn conn
|> put_view(Pleroma.Web.AdminAPI.StatusView) |> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false}) |> render("index.json", %{activities: activities, as: :activity})
end end
def list_user_statuses(conn, %{"nickname" => nickname} = params) do def list_user_statuses(conn, %{"nickname" => nickname} = params) do
@ -299,8 +300,8 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
}) })
conn conn
|> put_view(StatusView) |> put_view(MastodonAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false}) |> render("index.json", %{activities: activities, as: :activity})
else else
_ -> {:error, :not_found} _ -> {:error, :not_found}
end end
@ -829,14 +830,14 @@ def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
}) })
conn conn
|> put_view(Pleroma.Web.AdminAPI.StatusView) |> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false}) |> render("index.json", %{activities: activities, as: :activity})
end end
def status_show(conn, %{"id" => id}) do def status_show(conn, %{"id" => id}) do
with %Activity{} = activity <- Activity.get_by_id(id) do with %Activity{} = activity <- Activity.get_by_id(id) do
conn conn
|> put_view(StatusView) |> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity}) |> render("show.json", %{activity: activity})
else else
_ -> errors(conn, {:error, :not_found}) _ -> errors(conn, {:error, :not_found})
@ -861,7 +862,7 @@ def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
}) })
conn conn
|> put_view(StatusView) |> put_view(MastodonAPI.StatusView)
|> render("show.json", %{activity: activity}) |> render("show.json", %{activity: activity})
end end
end end

View file

@ -6,7 +6,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.MastodonAPI
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
def render("index.json", %{users: users, count: count, page_size: page_size}) do def render("index.json", %{users: users, count: count, page_size: page_size}) do
@ -119,6 +121,13 @@ def render("create-error.json", %{changeset: %Ecto.Changeset{changes: changes, e
} }
end end
def merge_account_views(%User{} = user) do
MastodonAPI.AccountView.render("show.json", %{user: user})
|> Map.merge(AdminAPI.AccountView.render("show.json", %{user: user}))
end
def merge_account_views(_), do: %{}
defp parse_error([]), do: "" defp parse_error([]), do: ""
defp parse_error(errors) do defp parse_error(errors) do

View file

@ -7,10 +7,13 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
alias Pleroma.HTML alias Pleroma.HTML
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.Report alias Pleroma.Web.AdminAPI.Report
alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
defdelegate merge_account_views(user), to: AdminAPI.AccountView
def render("index.json", %{reports: reports}) do def render("index.json", %{reports: reports}) do
%{ %{
reports: reports:
@ -41,8 +44,7 @@ def render("show.json", %{report: report, user: user, account: account, statuses
statuses: statuses:
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: statuses, activities: statuses,
as: :activity, as: :activity
skip_relationships: false
}), }),
state: report.data["state"], state: report.data["state"],
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes}) notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
@ -70,11 +72,4 @@ def render("show_note.json", %{
created_at: Utils.to_masto_date(inserted_at) created_at: Utils.to_masto_date(inserted_at)
} }
end end
defp merge_account_views(%User{} = user) do
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
end
defp merge_account_views(_), do: %{}
end end

View file

@ -7,24 +7,19 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.User alias Pleroma.Web.AdminAPI
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI
defdelegate merge_account_views(user), to: AdminAPI.AccountView
def render("index.json", opts) do def render("index.json", opts) do
safe_render_many(opts.activities, __MODULE__, "show.json", opts) safe_render_many(opts.activities, __MODULE__, "show.json", opts)
end end
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
user = StatusView.get_user(activity.data["actor"]) user = MastodonAPI.StatusView.get_user(activity.data["actor"])
StatusView.render("show.json", opts) MastodonAPI.StatusView.render("show.json", opts)
|> Map.merge(%{account: merge_account_views(user)}) |> Map.merge(%{account: merge_account_views(user)})
end end
defp merge_account_views(%User{} = user) do
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
end
defp merge_account_views(_), do: %{}
end end

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.ApiSpec.Helpers do defmodule Pleroma.Web.ApiSpec.Helpers do
alias OpenApiSpex.Operation alias OpenApiSpex.Operation
alias OpenApiSpex.Schema alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
def request_body(description, schema_ref, opts \\ []) do def request_body(description, schema_ref, opts \\ []) do
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"] media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
@ -47,6 +48,15 @@ def pagination_params do
] ]
end end
def with_relationships_param do
Operation.parameter(
:with_relationships,
:query,
BooleanLike,
"Embed relationships into accounts."
)
end
def empty_object_response do def empty_object_response do
Operation.response("Empty object", "application/json", %Schema{type: :object, example: %{}}) Operation.response("Empty object", "application/json", %Schema{type: :object, example: %{}})
end end

View file

@ -155,8 +155,10 @@ def followers_operation do
security: [%{"oAuth" => ["read:accounts"]}], security: [%{"oAuth" => ["read:accounts"]}],
description: description:
"Accounts which follow the given account, if network is not hidden by the account owner.", "Accounts which follow the given account, if network is not hidden by the account owner.",
parameters: parameters: [
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
with_relationships_param() | pagination_params()
],
responses: %{ responses: %{
200 => Operation.response("Accounts", "application/json", array_of_accounts()) 200 => Operation.response("Accounts", "application/json", array_of_accounts())
} }
@ -171,8 +173,10 @@ def following_operation do
security: [%{"oAuth" => ["read:accounts"]}], security: [%{"oAuth" => ["read:accounts"]}],
description: description:
"Accounts which the given account is following, if network is not hidden by the account owner.", "Accounts which the given account is following, if network is not hidden by the account owner.",
parameters: parameters: [
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(), %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
with_relationships_param() | pagination_params()
],
responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())} responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
} }
end end

View file

@ -19,6 +19,7 @@ def open_api_operation(action) do
apply(__MODULE__, operation, []) apply(__MODULE__, operation, [])
end end
# Note: `with_relationships` param is not supported (PleromaFE uses this op for autocomplete)
def account_search_operation do def account_search_operation do
%Operation{ %Operation{
tags: ["Search"], tags: ["Search"],
@ -96,8 +97,8 @@ def search_operation do
:query, :query,
%Schema{type: :integer}, %Schema{type: :integer},
"Offset" "Offset"
) ),
| pagination_params() with_relationships_param() | pagination_params()
], ],
responses: %{ responses: %{
200 => Operation.response("Results", "application/json", results()) 200 => Operation.response("Results", "application/json", results())
@ -138,8 +139,8 @@ def search2_operation do
:query, :query,
%Schema{allOf: [BooleanLike], default: false}, %Schema{allOf: [BooleanLike], default: false},
"Only include accounts that the user is following" "Only include accounts that the user is following"
) ),
| pagination_params() with_relationships_param() | pagination_params()
], ],
responses: %{ responses: %{
200 => Operation.response("Results", "application/json", results2()) 200 => Operation.response("Results", "application/json", results2())

View file

@ -7,7 +7,6 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
alias OpenApiSpex.Schema alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.AccountOperation alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.Status
@ -349,10 +348,7 @@ def bookmarks_operation do
summary: "Bookmarked statuses", summary: "Bookmarked statuses",
description: "Statuses the user has bookmarked", description: "Statuses the user has bookmarked",
operationId: "StatusController.bookmarks", operationId: "StatusController.bookmarks",
parameters: [ parameters: pagination_params(),
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
| pagination_params()
],
security: [%{"oAuth" => ["read:bookmarks"]}], security: [%{"oAuth" => ["read:bookmarks"]}],
responses: %{ responses: %{
200 => Operation.response("Array of Statuses", "application/json", array_of_statuses()) 200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())

View file

@ -27,8 +27,7 @@ def home_operation do
local_param(), local_param(),
with_muted_param(), with_muted_param(),
exclude_visibilities_param(), exclude_visibilities_param(),
reply_visibility_param(), reply_visibility_param() | pagination_params()
with_relationships_param() | pagination_params()
], ],
operationId: "TimelineController.home", operationId: "TimelineController.home",
responses: %{ responses: %{
@ -63,8 +62,7 @@ def public_operation do
only_media_param(), only_media_param(),
with_muted_param(), with_muted_param(),
exclude_visibilities_param(), exclude_visibilities_param(),
reply_visibility_param(), reply_visibility_param() | pagination_params()
with_relationships_param() | pagination_params()
], ],
operationId: "TimelineController.public", operationId: "TimelineController.public",
responses: %{ responses: %{
@ -109,8 +107,7 @@ def hashtag_operation do
local_param(), local_param(),
only_media_param(), only_media_param(),
with_muted_param(), with_muted_param(),
exclude_visibilities_param(), exclude_visibilities_param() | pagination_params()
with_relationships_param() | pagination_params()
], ],
operationId: "TimelineController.hashtag", operationId: "TimelineController.hashtag",
responses: %{ responses: %{
@ -134,8 +131,7 @@ def list_operation do
required: true required: true
), ),
with_muted_param(), with_muted_param(),
exclude_visibilities_param(), exclude_visibilities_param() | pagination_params()
with_relationships_param() | pagination_params()
], ],
operationId: "TimelineController.list", operationId: "TimelineController.list",
responses: %{ responses: %{
@ -153,10 +149,6 @@ defp array_of_statuses do
} }
end end
defp with_relationships_param do
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
end
defp local_param do defp local_param do
Operation.parameter( Operation.parameter(
:local, :local,

View file

@ -23,6 +23,7 @@ def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}}
if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
author = User.get_cached_by_nickname(user_name) author = User.get_cached_by_nickname(user_name)
author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author) author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author)
message = ChatChannelState.add_message(%{text: text, author: author}) message = ChatChannelState.add_message(%{text: text, author: author})
broadcast!(socket, "new_msg", message) broadcast!(socket, "new_msg", message)

View file

@ -5,8 +5,6 @@
defmodule Pleroma.Web.ControllerHelper do defmodule Pleroma.Web.ControllerHelper do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Config
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html # As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"] @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
@ -106,13 +104,16 @@ def put_if_exist(map, _key, nil), do: map
def put_if_exist(map, key, value), do: Map.put(map, key, value) def put_if_exist(map, key, value), do: Map.put(map, key, value)
@doc "Whether to skip rendering `[:account][:pleroma][:relationship]`for statuses/notifications" @doc """
def skip_relationships?(params) do Returns true if request specifies to include embedded relationships in account objects.
if Config.get([:extensions, :output_relationships_in_statuses_by_default]) do May only be used in selected account-related endpoints; has no effect for status- or
false notification-related endpoints.
else """
# BREAKING: older PleromaFE versions do not send this param but _do_ expect relationships. # Intended for PleromaFE: https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838
not truthy_param?(params["with_relationships"]) def embed_relationships?(params) do
end # To do once OpenAPI transition mess is over: just `truthy_param?(params[:with_relationships])`
params
|> Map.get(:with_relationships, params["with_relationships"])
|> truthy_param?()
end end
end end

View file

@ -10,8 +10,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
add_link_headers: 2, add_link_headers: 2,
truthy_param?: 1, truthy_param?: 1,
assign_account_by_id: 2, assign_account_by_id: 2,
json_response: 3, embed_relationships?: 1,
skip_relationships?: 1 json_response: 3
] ]
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
@ -247,8 +247,7 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: reading_user, for: reading_user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
else else
_e -> render_error(conn, :not_found, "Can't find user") _e -> render_error(conn, :not_found, "Can't find user")
@ -271,7 +270,13 @@ def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do
conn conn
|> add_link_headers(followers) |> add_link_headers(followers)
|> render("index.json", for: for_user, users: followers, as: :user) # https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
|> render("index.json",
for: for_user,
users: followers,
as: :user,
embed_relationships: embed_relationships?(params)
)
end end
@doc "GET /api/v1/accounts/:id/following" @doc "GET /api/v1/accounts/:id/following"
@ -290,7 +295,13 @@ def following(%{assigns: %{user: for_user, account: user}} = conn, params) do
conn conn
|> add_link_headers(followers) |> add_link_headers(followers)
|> render("index.json", for: for_user, users: followers, as: :user) # https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
|> render("index.json",
for: for_user,
users: followers,
as: :user,
embed_relationships: embed_relationships?(params)
)
end end
@doc "GET /api/v1/accounts/:id/lists" @doc "GET /api/v1/accounts/:id/lists"

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Web.MastodonAPI.NotificationController do defmodule Pleroma.Web.MastodonAPI.NotificationController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1] import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
@ -50,8 +50,7 @@ def index(%{assigns: %{user: user}} = conn, params) do
|> add_link_headers(notifications) |> add_link_headers(notifications)
|> render("index.json", |> render("index.json",
notifications: notifications, notifications: notifications,
for: user, for: user
skip_relationships: skip_relationships?(params)
) )
end end

View file

@ -5,14 +5,13 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [skip_relationships?: 1]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter alias Pleroma.Plugs.RateLimiter
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.ControllerHelper
alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
@ -34,7 +33,11 @@ def account_search(%{assigns: %{user: user}} = conn, %{q: query} = params) do
conn conn
|> put_view(AccountView) |> put_view(AccountView)
|> render("index.json", users: accounts, for: user, as: :user) |> render("index.json",
users: accounts,
for: user,
as: :user
)
end end
def search2(conn, params), do: do_search(:v2, conn, params) def search2(conn, params), do: do_search(:v2, conn, params)
@ -71,13 +74,13 @@ defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params)
defp search_options(params, user) do defp search_options(params, user) do
[ [
skip_relationships: skip_relationships?(params),
resolve: params[:resolve], resolve: params[:resolve],
following: params[:following], following: params[:following],
limit: params[:limit], limit: params[:limit],
offset: params[:offset], offset: params[:offset],
type: params[:type], type: params[:type],
author: get_author(params), author: get_author(params),
embed_relationships: ControllerHelper.embed_relationships?(params),
for_user: user for_user: user
] ]
|> Enum.filter(&elem(&1, 1)) |> Enum.filter(&elem(&1, 1))
@ -90,7 +93,7 @@ defp resource_search(_, "accounts", query, options) do
users: accounts, users: accounts,
for: options[:for_user], for: options[:for_user],
as: :user, as: :user,
skip_relationships: false embed_relationships: options[:embed_relationships]
) )
end end
@ -100,8 +103,7 @@ defp resource_search(_, "statuses", query, options) do
StatusView.render("index.json", StatusView.render("index.json",
activities: statuses, activities: statuses,
for: options[:for_user], for: options[:for_user],
as: :activity, as: :activity
skip_relationships: options[:skip_relationships]
) )
end end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [try_render: 3, add_link_headers: 2, skip_relationships?: 1] only: [try_render: 3, add_link_headers: 2]
require Ecto.Query require Ecto.Query
@ -105,7 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
`ids` query param is required `ids` query param is required
""" """
def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do def index(%{assigns: %{user: user}} = conn, %{ids: ids} = _params) do
limit = 100 limit = 100
activities = activities =
@ -117,8 +117,7 @@ def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do
render(conn, "index.json", render(conn, "index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
@ -383,8 +382,7 @@ def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
@ -406,8 +404,7 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
end end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2, add_link_headers: 3, skip_relationships?: 1] only: [add_link_headers: 2, add_link_headers: 3]
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
@ -63,8 +63,7 @@ def home(%{assigns: %{user: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
@ -88,8 +87,7 @@ def direct(%{assigns: %{user: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
@ -125,8 +123,7 @@ def public(%{assigns: %{user: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
end end
@ -173,8 +170,7 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end
@ -203,8 +199,7 @@ def list(%{assigns: %{user: user}} = conn, %{list_id: id} = params) do
render(conn, "index.json", render(conn, "index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
else else
_e -> render_error(conn, :forbidden, "Error.") _e -> render_error(conn, :forbidden, "Error.")

View file

@ -15,13 +15,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
def render("index.json", %{users: users} = opts) do def render("index.json", %{users: users} = opts) do
reading_user = opts[:for] reading_user = opts[:for]
# Note: :skip_relationships option is currently intentionally not supported for accounts
relationships_opt = relationships_opt =
cond do cond do
Map.has_key?(opts, :relationships) -> Map.has_key?(opts, :relationships) ->
opts[:relationships] opts[:relationships]
is_nil(reading_user) -> is_nil(reading_user) || !opts[:embed_relationships] ->
UserRelationship.view_relationships_option(nil, []) UserRelationship.view_relationships_option(nil, [])
true -> true ->
@ -193,14 +192,14 @@ defp do_render("show.json", %{user: user} = opts) do
end) end)
relationship = relationship =
if opts[:skip_relationships] do if opts[:embed_relationships] do
%{}
else
render("relationship.json", %{ render("relationship.json", %{
user: opts[:for], user: opts[:for],
target: user, target: user,
relationships: opts[:relationships] relationships: opts[:relationships]
}) })
else
%{}
end end
%{ %{

View file

@ -51,9 +51,7 @@ def render("index.json", %{notifications: notifications, for: reading_user} = op
|> Enum.filter(& &1) |> Enum.filter(& &1)
|> Kernel.++(move_activities_targets) |> Kernel.++(move_activities_targets)
UserRelationship.view_relationships_option(reading_user, actors, UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
source_mutes_only: opts[:skip_relationships]
)
end end
opts = opts =
@ -83,15 +81,13 @@ def render(
mastodon_type = Activity.mastodon_notification_type(activity) mastodon_type = Activity.mastodon_notification_type(activity)
render_opts = %{ # Note: :relationships contain user mutes (needed for :muted flag in :status)
relationships: opts[:relationships], status_render_opts = %{relationships: opts[:relationships]}
skip_relationships: opts[:skip_relationships]
}
with %{id: _} = account <- with %{id: _} = account <-
AccountView.render( AccountView.render(
"show.json", "show.json",
Map.merge(render_opts, %{user: actor, for: reading_user}) %{user: actor, for: reading_user}
) do ) do
response = %{ response = %{
id: to_string(notification.id), id: to_string(notification.id),
@ -105,21 +101,20 @@ def render(
case mastodon_type do case mastodon_type do
"mention" -> "mention" ->
put_status(response, activity, reading_user, render_opts) put_status(response, activity, reading_user, status_render_opts)
"favourite" -> "favourite" ->
put_status(response, parent_activity_fn.(), reading_user, render_opts) put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
"reblog" -> "reblog" ->
put_status(response, parent_activity_fn.(), reading_user, render_opts) put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
"move" -> "move" ->
# Note: :skip_relationships option being applied to _account_ rendering (here) put_target(response, activity, reading_user, %{})
put_target(response, activity, reading_user, render_opts)
"pleroma:emoji_reaction" -> "pleroma:emoji_reaction" ->
response response
|> put_status(parent_activity_fn.(), reading_user, render_opts) |> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|> put_emoji(activity) |> put_emoji(activity)
type when type in ["follow", "follow_request"] -> type when type in ["follow", "follow_request"] ->

View file

@ -107,9 +107,7 @@ def render("index.json", opts) do
|> Enum.map(&get_user(&1.data["actor"], false)) |> Enum.map(&get_user(&1.data["actor"], false))
|> Enum.filter(& &1) |> Enum.filter(& &1)
UserRelationship.view_relationships_option(reading_user, actors, UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
source_mutes_only: opts[:skip_relationships]
)
end end
opts = opts =
@ -162,9 +160,7 @@ def render(
account: account:
AccountView.render("show.json", %{ AccountView.render("show.json", %{
user: user, user: user,
for: opts[:for], for: opts[:for]
relationships: opts[:relationships],
skip_relationships: opts[:skip_relationships]
}), }),
in_reply_to_id: nil, in_reply_to_id: nil,
in_reply_to_account_id: nil, in_reply_to_account_id: nil,
@ -330,9 +326,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
account: account:
AccountView.render("show.json", %{ AccountView.render("show.json", %{
user: user, user: user,
for: opts[:for], for: opts[:for]
relationships: opts[:relationships],
skip_relationships: opts[:skip_relationships]
}), }),
in_reply_to_id: reply_to && to_string(reply_to.id), 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), in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1] only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
alias Ecto.Changeset alias Ecto.Changeset
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
@ -149,8 +149,7 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: for_user, for: for_user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
end end

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1] import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
@ -69,7 +69,12 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}
%{ %{
name: emoji, name: emoji,
count: length(users), count: length(users),
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), accounts:
AccountView.render("index.json", %{
users: users,
for: user,
as: :user
}),
me: !!(user && user.ap_id in user_ap_ids) me: !!(user && user.ap_id in user_ap_ids)
} }
end end
@ -145,8 +150,7 @@ def conversation_statuses(
|> render("index.json", |> render("index.json",
activities: activities, activities: activities,
for: user, for: user,
as: :activity, as: :activity
skip_relationships: skip_relationships?(params)
) )
else else
_error -> _error ->
@ -201,7 +205,7 @@ def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notif
end end
end end
def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
with notifications <- Notification.set_read_up_to(user, max_id) do with notifications <- Notification.set_read_up_to(user, max_id) do
notifications = Enum.take(notifications, 80) notifications = Enum.take(notifications, 80)
@ -209,8 +213,7 @@ def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => m
|> put_view(NotificationView) |> put_view(NotificationView)
|> render("index.json", |> render("index.json",
notifications: notifications, notifications: notifications,
for: user, for: user
skip_relationships: skip_relationships?(params)
) )
end end
end end

View file

@ -12,9 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
import Pleroma.Factory import Pleroma.Factory
test "does NOT render account/pleroma/relationship if this is disabled by default" do test "does NOT render account/pleroma/relationship by default" do
clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
%{user: user, conn: conn} = oauth_access(["read:notifications"]) %{user: user, conn: conn} = oauth_access(["read:notifications"])
other_user = insert(:user) other_user = insert(:user)

View file

@ -1176,7 +1176,7 @@ test "replaces missing description with an empty string", %{conn: conn, user: us
end end
test "bookmarks" do test "bookmarks" do
bookmarks_uri = "/api/v1/bookmarks?with_relationships=true" bookmarks_uri = "/api/v1/bookmarks"
%{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"]) %{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])
author = insert(:user) author = insert(:user)

View file

@ -20,12 +20,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
describe "home" do describe "home" do
setup do: oauth_access(["read:statuses"]) setup do: oauth_access(["read:statuses"])
test "does NOT render account/pleroma/relationship if this is disabled by default", %{ test "does NOT embed account/pleroma/relationship in statuses", %{
user: user, user: user,
conn: conn conn: conn
} do } do
clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
other_user = insert(:user) other_user = insert(:user)
{:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"})
@ -41,72 +39,6 @@ test "does NOT render account/pleroma/relationship if this is disabled by defaul
end) end)
end end
test "the home timeline", %{user: user, conn: conn} do
uri = "/api/v1/timelines/home?with_relationships=1"
following = insert(:user, nickname: "followed")
third_user = insert(:user, nickname: "repeated")
{:ok, _activity} = CommonAPI.post(following, %{status: "post"})
{:ok, activity} = CommonAPI.post(third_user, %{status: "repeated post"})
{:ok, _, _} = CommonAPI.repeat(activity.id, following)
ret_conn = get(conn, uri)
assert Enum.empty?(json_response_and_validate_schema(ret_conn, :ok))
{:ok, _user} = User.follow(user, following)
ret_conn = get(conn, uri)
assert [
%{
"reblog" => %{
"content" => "repeated post",
"account" => %{
"pleroma" => %{
"relationship" => %{"following" => false, "followed_by" => false}
}
}
},
"account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
},
%{
"content" => "post",
"account" => %{
"acct" => "followed",
"pleroma" => %{"relationship" => %{"following" => true}}
}
}
] = json_response_and_validate_schema(ret_conn, :ok)
{:ok, _user} = User.follow(third_user, user)
ret_conn = get(conn, uri)
assert [
%{
"reblog" => %{
"content" => "repeated post",
"account" => %{
"acct" => "repeated",
"pleroma" => %{
"relationship" => %{"following" => false, "followed_by" => true}
}
}
},
"account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
},
%{
"content" => "post",
"account" => %{
"acct" => "followed",
"pleroma" => %{"relationship" => %{"following" => true}}
}
}
] = json_response_and_validate_schema(ret_conn, :ok)
end
test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do
{:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})

View file

@ -302,82 +302,6 @@ test "represent a relationship for the user with a pending follow request" do
end end
end end
test "represent an embedded relationship" do
user =
insert(:user, %{
follower_count: 0,
note_count: 5,
actor_type: "Service",
nickname: "shp@shitposter.club",
inserted_at: ~N[2017-08-15 15:47:06.597036]
})
other_user = insert(:user)
{:ok, other_user} = User.follow(other_user, user)
{:ok, _user_relationship} = User.block(other_user, user)
{:ok, _} = User.follow(insert(:user), user)
expected = %{
id: to_string(user.id),
username: "shp",
acct: user.nickname,
display_name: user.name,
locked: false,
created_at: "2017-08-15T15:47:06.000Z",
followers_count: 1,
following_count: 0,
statuses_count: 5,
note: user.bio,
url: user.ap_id,
avatar: "http://localhost:4001/images/avi.png",
avatar_static: "http://localhost:4001/images/avi.png",
header: "http://localhost:4001/images/banner.png",
header_static: "http://localhost:4001/images/banner.png",
emojis: [],
fields: [],
bot: true,
source: %{
note: user.bio,
sensitive: false,
pleroma: %{
actor_type: "Service",
discoverable: false
},
fields: []
},
pleroma: %{
background_image: nil,
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
hide_favorites: true,
hide_followers: false,
hide_follows: false,
hide_followers_count: false,
hide_follows_count: false,
relationship: %{
id: to_string(user.id),
following: false,
followed_by: false,
blocking: true,
blocked_by: false,
subscribing: false,
muting: false,
muting_notifications: false,
requested: false,
domain_blocking: false,
showing_reblogs: true,
endorsed: false
},
skip_thread_containment: false
}
}
assert expected ==
AccountView.render("show.json", %{user: refresh_record(user), for: other_user})
end
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
user = insert(:user, pleroma_settings_store: %{fe: "test"}) user = insert(:user, pleroma_settings_store: %{fe: "test"})

View file

@ -42,7 +42,11 @@ test "Mention notification" do
id: to_string(notification.id), id: to_string(notification.id),
pleroma: %{is_seen: false}, pleroma: %{is_seen: false},
type: "mention", type: "mention",
account: AccountView.render("show.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}), status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}),
created_at: Utils.to_masto_date(notification.inserted_at) created_at: Utils.to_masto_date(notification.inserted_at)
} }

View file

@ -576,7 +576,7 @@ test "a rich media card with all relevant data renders correctly" do
end end
end end
test "embeds a relationship in the account" do test "does not embed a relationship in the account" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
@ -587,13 +587,11 @@ test "embeds a relationship in the account" do
result = StatusView.render("show.json", %{activity: activity, for: other_user}) result = StatusView.render("show.json", %{activity: activity, for: other_user})
assert result[:account][:pleroma][:relationship] == assert result[:account][:pleroma][:relationship] == %{}
AccountView.render("relationship.json", %{user: other_user, target: user})
assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
end end
test "embeds a relationship in the account in reposts" do test "does not embed a relationship in the account in reposts" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
@ -606,12 +604,8 @@ test "embeds a relationship in the account in reposts" do
result = StatusView.render("show.json", %{activity: activity, for: user}) result = StatusView.render("show.json", %{activity: activity, for: user})
assert result[:account][:pleroma][:relationship] == assert result[:account][:pleroma][:relationship] == %{}
AccountView.render("relationship.json", %{user: user, target: other_user}) assert result[:reblog][:account][:pleroma][:relationship] == %{}
assert result[:reblog][:account][:pleroma][:relationship] ==
AccountView.render("relationship.json", %{user: user, target: user})
assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
end end