From 75c8036d2cf5a7c806168089ee757aab10ea32c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 26 May 2022 22:42:43 +0200 Subject: [PATCH 1/4] Count post quotes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/object.ex | 54 +++++++++++++++++-- lib/pleroma/web/activity_pub/activity_pub.ex | 12 +++++ .../object_validators/common_fields.ex | 1 + lib/pleroma/web/activity_pub/side_effects.ex | 8 +++ lib/pleroma/web/api_spec/schemas/status.ex | 7 ++- .../web/mastodon_api/views/status_view.ex | 3 +- 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index fe264b5e0b..6a597277fe 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -301,10 +301,6 @@ def increase_replies_count(ap_id) do end end - defp poll_is_multiple?(%Object{data: %{"anyOf" => [_ | _]}}), do: true - - defp poll_is_multiple?(_), do: false - def decrease_replies_count(ap_id) do Object |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) @@ -328,6 +324,56 @@ def decrease_replies_count(ap_id) do end end + def increase_quotes_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + safe_jsonb_set(?, '{quotesCount}', + (coalesce((?->>'quotesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def decrease_quotes_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + safe_jsonb_set(?, '{quotesCount}', + (greatest(0, (?->>'quotesCount')::int - 1))::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + defp poll_is_multiple?(%Object{data: %{"anyOf" => [_ | _]}}), do: true + + defp poll_is_multiple?(_), do: false + def increase_vote_count(ap_id, name, actor) do with %Object{} = object <- Object.normalize(ap_id, fetch: false), "Question" <- object.data["type"] do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 27581bf1fd..05bd5293b6 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -97,6 +97,17 @@ defp increase_replies_count_if_reply(%{ defp increase_replies_count_if_reply(_create_data), do: :noop + defp increase_quotes_count_if_quote(%{ + "object" => %{"quoteUrl" => quote_ap_id} = object, + "type" => "Create" + }) do + if is_public?(object) do + Object.increase_quotes_count(quote_ap_id) + end + end + + defp increase_quotes_count_if_quote(_create_data), do: :noop + @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page] @impl true def persist(%{"type" => type} = object, meta) when type in @object_types do @@ -291,6 +302,7 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param with {:ok, activity} <- insert(create_data, local, fake), {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), + _ <- increase_quotes_count_if_quote(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), {:ok, _actor} <- update_last_status_at_if_public(actor, activity), diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 1850887aa6..230f90a581 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -58,6 +58,7 @@ defmacro status_object_fields do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) + field(:quote_count, :integer, default: 0) field(:inReplyTo, ObjectValidators.ObjectID) field(:quoteUrl, ObjectValidators.ObjectID) field(:url, ObjectValidators.Uri) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 97c653902c..26bef72708 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -205,6 +205,10 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do Object.increase_replies_count(in_reply_to) end + if quote_url = object.data["quoteUrl"] do + Object.increase_quotes_count(quote_url) + end + reply_depth = (meta[:depth] || 0) + 1 # FIXME: Force inReplyTo to replies @@ -306,6 +310,10 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, Object.decrease_replies_count(in_reply_to) end + if quote_url = deleted_object.data["quoteUrl"] do + Object.decrease_quotes_count(quote_url) + end + MessageReference.delete_for_object(deleted_object) ap_streamer().stream_out(object) diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 017df9ac79..5189cc15d7 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -192,6 +192,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :boolean, description: "`true` if the quoted post is visible to the user" }, + quote_count: %Schema{ + type: :integer, + description: "How many statuses quoted this status" + }, local: %Schema{ type: :boolean, description: "`true` if the post was made on the local instance" @@ -346,7 +350,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "in_reply_to_account_acct" => nil, "local" => true, "spoiler_text" => %{"text/plain" => ""}, - "thread_muted" => false + "thread_muted" => false, + "quote_count" => 0 }, "poll" => nil, "reblog" => nil, diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 1be452e7ff..a7538380f2 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -416,7 +416,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} emoji_reactions: emoji_reactions, parent_visible: visible_for_user?(reply_to, opts[:for]), pinned_at: pinned_at, - content_type: opts[:with_source] && (object.data["content_type"] || "text/plain") + content_type: opts[:with_source] && (object.data["content_type"] || "text/plain"), + quote_count: object.data["quotesCount"] || 0, } } end From 8d1617f4392574580d0cee76c5843c9c59885570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 May 2022 11:27:48 +0200 Subject: [PATCH 2/4] Add a route for status quotes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/activity_pub.ex | 14 +++++ .../object_validators/common_fields.ex | 2 +- .../operations/pleroma_status_operation.ex | 45 ++++++++++++++ lib/pleroma/web/api_spec/schemas/status.ex | 4 +- .../web/mastodon_api/views/status_view.ex | 2 +- .../controllers/status_controller.ex | 61 +++++++++++++++++++ lib/pleroma/web/router.ex | 2 + 7 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex create mode 100644 lib/pleroma/web/pleroma_api/controllers/status_controller.ex diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 05bd5293b6..3add644c19 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1215,6 +1215,16 @@ defp restrict_filtered(query, %{blocking_user: %User{} = user}) do defp restrict_filtered(query, _), do: query + defp restrict_quote_url(query, %{quote_url: quote_url}) do + IO.inspect(quote_url) + + from([_activity, object] in query, + where: fragment("(?)->'quoteUrl' = ?", object.data, ^quote_url) + ) + end + + defp restrict_quote_url(query, _), do: query + defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query defp exclude_poll_votes(query, _) do @@ -1378,6 +1388,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do |> restrict_instance(opts) |> restrict_announce_object_actor(opts) |> restrict_filtered(opts) + |> restrict_quote_url(opts) |> Activity.restrict_deactivated_users() |> exclude_poll_votes(opts) |> exclude_chat_messages(opts) @@ -1794,4 +1805,7 @@ def fetch_direct_messages_query do |> restrict_visibility(%{visibility: "direct"}) |> order_by([activity], asc: activity.id) end + + def fetch_quotes(%Activity{data: %{"id" => ap_id}} = activity, params) do + end end diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 230f90a581..cb5851f201 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -58,7 +58,7 @@ defmacro status_object_fields do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) - field(:quote_count, :integer, default: 0) + field(:quotes_count, :integer, default: 0) field(:inReplyTo, ObjectValidators.ObjectID) field(:quoteUrl, ObjectValidators.ObjectID) field(:url, ObjectValidators.Uri) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex new file mode 100644 index 0000000000..54eb57ce3b --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.StatusOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def quotes_operation do + %Operation{ + tags: ["Retrieve status information"], + summary: "Quoted by", + description: "View quotes for a given status", + operationId: "PleromaAPI.StatusController.quotes", + parameters: [id_param() | pagination_params()], + security: [%{"oAuth" => ["read:statuses"]}], + responses: %{ + 200 => + Operation.response( + "Array of Status", + "application/json", + StatusOperation.array_of_statuses() + ), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def id_param do + Operation.parameter(:id, :path, FlakeID, "Status ID", + example: "9umDrYheeY451cQnEe", + required: true + ) + end +end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 5189cc15d7..604ed9cbe7 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -192,7 +192,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do type: :boolean, description: "`true` if the quoted post is visible to the user" }, - quote_count: %Schema{ + quotes_count: %Schema{ type: :integer, description: "How many statuses quoted this status" }, @@ -351,7 +351,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "local" => true, "spoiler_text" => %{"text/plain" => ""}, "thread_muted" => false, - "quote_count" => 0 + "quotes_count" => 0 }, "poll" => nil, "reblog" => nil, diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a7538380f2..71b29bb3c7 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -417,7 +417,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} parent_visible: visible_for_user?(reply_to, opts[:for]), pinned_at: pinned_at, content_type: opts[:with_source] && (object.data["content_type"] || "text/plain"), - quote_count: object.data["quotesCount"] || 0, + quotes_count: object.data["quotesCount"] || 0 } } end diff --git a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex new file mode 100644 index 0000000000..f5906b94b8 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.StatusController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] + + require Ecto.Query + require Pleroma.Constants + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :quotes + ) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaStatusOperation + + @doc "GET /api/v1/pleroma/statuses/:id/quotes" + def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do + with %Activity{object: object} <- Activity.get_by_id_with_object(id) do + params = + params + |> Map.put(:type, "Create") + |> Map.put(:blocking_user, user) + |> Map.put(:quote_url, object.data["id"]) + + recipients = + if user do + [Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)] + 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: user, + as: :activity + ) + end + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index be8988a4a4..22d0e3ecb5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -476,6 +476,8 @@ defmodule Pleroma.Web.Router do pipe_through(:api) get("/accounts/:id/favourites", AccountController, :favourites) get("/accounts/:id/endorsements", AccountController, :endorsements) + + get("/statuses/:id/quotes", StatusController, :quotes) end scope [] do From cd13b92a3b66af08a7bfd3d1a8b9a906ed0cdaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 May 2022 13:51:46 +0200 Subject: [PATCH 3/4] Add index for quoteUrl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 -- .../20220527134341_add_quote_url_index_to_objects.exs | 11 +++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 3add644c19..3f94c59b25 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1216,8 +1216,6 @@ defp restrict_filtered(query, %{blocking_user: %User{} = user}) do defp restrict_filtered(query, _), do: query defp restrict_quote_url(query, %{quote_url: quote_url}) do - IO.inspect(quote_url) - from([_activity, object] in query, where: fragment("(?)->'quoteUrl' = ?", object.data, ^quote_url) ) diff --git a/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs b/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs new file mode 100644 index 0000000000..c746f12a14 --- /dev/null +++ b/priv/repo/migrations/20220527134341_add_quote_url_index_to_objects.exs @@ -0,0 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.AddQuoteUrlIndexToObjects do + use Ecto.Migration + + def change do + create_if_not_exists(index(:objects, ["(data->'quoteUrl')"], name: :objects_quote_url)) + end +end From 0fbe33b4aafa8f78886607c59c489b137c48536e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 May 2022 14:46:26 +0200 Subject: [PATCH 4/4] Add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/activity_pub.ex | 3 -- .../operations/pleroma_status_operation.ex | 2 +- .../controllers/status_controller.ex | 7 ++- .../web/activity_pub/activity_pub_test.exs | 28 ++++++++++ .../mastodon_api/views/status_view_test.exs | 3 +- .../controllers/status_controller_test.exs | 54 +++++++++++++++++++ 6 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 test/pleroma/web/pleroma_api/controllers/status_controller_test.exs diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 3f94c59b25..ffc09ceb17 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1803,7 +1803,4 @@ def fetch_direct_messages_query do |> restrict_visibility(%{visibility: "direct"}) |> order_by([activity], asc: activity.id) end - - def fetch_quotes(%Activity{data: %{"id" => ap_id}} = activity, params) do - end end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex index 54eb57ce3b..6e69c52694 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex @@ -4,9 +4,9 @@ defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do alias OpenApiSpex.Operation - alias Pleroma.Web.ApiSpec.StatusOperation alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.StatusOperation import Pleroma.Web.ApiSpec.Helpers diff --git a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex index f5906b94b8..482662fdd2 100644 --- a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.PleromaAPI.StatusController do alias Pleroma.Activity alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.Plugs.OAuthScopesPlug @@ -29,7 +30,8 @@ defmodule Pleroma.Web.PleromaAPI.StatusController do @doc "GET /api/v1/pleroma/statuses/:id/quotes" def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do - with %Activity{object: object} <- Activity.get_by_id_with_object(id) do + with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do params = params |> Map.put(:type, "Create") @@ -56,6 +58,9 @@ def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do for: user, as: :activity ) + else + nil -> {:error, :not_found} + false -> {:error, :not_found} end end end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 3064ffc810..e454c8dd0b 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -794,6 +794,34 @@ test "increases replies count", %{user: user} do assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 end + + test "increates quotes count", %{user: user} do + user2 = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) + ap_id = activity.data["id"] + quote_data = %{status: "1", quote_id: activity.id} + + # public + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "public")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 1 + + # unlisted + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "unlisted")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + + # private + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "private")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + + # direct + {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "direct")) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert object.data["quotesCount"] == 2 + end end describe "fetch activities for recipients" do diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index 87b79aeb85..08846abb3e 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -292,7 +292,8 @@ test "a note activity" do emoji_reactions: [], parent_visible: false, pinned_at: nil, - content_type: nil + content_type: nil, + quotes_count: 0 } } diff --git a/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs new file mode 100644 index 0000000000..f942f05567 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.StatusControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "getting quotes of a specified post" do + setup do + [current_user, user] = insert_pair(:user) + %{user: current_user, conn: conn} = oauth_access(["read:statuses"], user: current_user) + [current_user: current_user, user: user, conn: conn] + end + + test "shows quotes of a post", %{conn: conn} do + user = insert(:user) + activity = insert(:note_activity) + + {:ok, quote_post} = CommonAPI.post(user, %{status: "quoat", quote_id: activity.id}) + + response = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/quotes") + |> json_response_and_validate_schema(:ok) + + [status] = response + + assert length(response) == 1 + assert status["id"] == quote_post.id + end + + test "returns 404 error when a post can't be seen", %{conn: conn} do + activity = insert(:direct_note_activity) + + response = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/quotes") + + assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"} + end + + test "returns 404 error when a post does not exist", %{conn: conn} do + response = + conn + |> get("/api/v1/pleroma/statuses/idontexist/quotes") + + assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"} + end + end +end