fix(activitypub): deduplicate account favourites
This commit is contained in:
parent
9a6c2f909d
commit
99befcaf1b
3 changed files with 82 additions and 0 deletions
|
|
@ -1025,6 +1025,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp restrict_assigned_account(query, _), do: query
|
||||
|
||||
defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
|
||||
newer_favorite =
|
||||
from(newer_favorite in Activity,
|
||||
where: newer_favorite.actor == ^ap_id,
|
||||
where: newer_favorite.id > parent_as(:favorited_activity).id,
|
||||
where: fragment("?->>'type' = ?", newer_favorite.data, "Like"),
|
||||
where:
|
||||
fragment(
|
||||
"associated_object_id(?) = associated_object_id(?)",
|
||||
newer_favorite.data,
|
||||
parent_as(:favorited_activity).data
|
||||
),
|
||||
select: 1
|
||||
)
|
||||
|
||||
from(
|
||||
[activity, object: object] in query,
|
||||
join: favorited_activity in Activity,
|
||||
|
|
@ -1033,6 +1047,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
favorited_activity.actor == ^ap_id and
|
||||
fragment("?->>'type' = ?", favorited_activity.data, "Like") and
|
||||
fragment("associated_object_id(?) = (?)->>'id'", favorited_activity.data, object.data),
|
||||
where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id),
|
||||
where: not exists(subquery(newer_favorite)),
|
||||
select: %Activity{activity | pagination_id: favorited_activity.id}
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddActivityFavoritesLookupIndex do
|
||||
use Ecto.Migration
|
||||
|
||||
@disable_ddl_transaction true
|
||||
@disable_migration_lock true
|
||||
|
||||
def change do
|
||||
create_if_not_exists(
|
||||
index(:activities, [:actor, "associated_object_id(data)", "id DESC"],
|
||||
name: :activities_favorites_lookup_index,
|
||||
where: "data->>'type' = 'Like'",
|
||||
concurrently: true
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -5,9 +5,12 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
|
|
@ -127,6 +130,49 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
|
|||
assert Enum.map(response, & &1["id"]) == [second_post.id, first_post.id, third_post.id]
|
||||
end
|
||||
|
||||
test "returns a status once when duplicate favorite activities exist", %{
|
||||
conn: conn,
|
||||
user: user
|
||||
} do
|
||||
older_activity = insert(:note_activity)
|
||||
{:ok, _} = CommonAPI.favorite(older_activity.id, user)
|
||||
|
||||
activity = insert(:note_activity)
|
||||
{:ok, favorite} = CommonAPI.favorite(activity.id, user)
|
||||
|
||||
duplicate =
|
||||
Repo.insert!(%Activity{
|
||||
actor: favorite.actor,
|
||||
data: Map.put(favorite.data, "id", Utils.generate_activity_id()),
|
||||
local: favorite.local,
|
||||
recipients: favorite.recipients
|
||||
})
|
||||
|
||||
conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites?limit=1")
|
||||
response = json_response_and_validate_schema(conn, :ok)
|
||||
|
||||
assert Enum.map(response, & &1["id"]) == [activity.id]
|
||||
assert [link_header] = get_resp_header(conn, "link")
|
||||
assert link_header =~ "max_id=#{duplicate.id}"
|
||||
assert link_header =~ "min_id=#{duplicate.id}"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites?max_id=#{duplicate.id}&limit=1")
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert Enum.map(response, & &1["id"]) == [older_activity.id]
|
||||
|
||||
{:ok, _} = CommonAPI.unfavorite(activity.id, user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert Enum.map(response, & &1["id"]) == [older_activity.id]
|
||||
end
|
||||
|
||||
test "returns favorited DM only when user is logged in and he is one of recipients", %{
|
||||
current_user: current_user,
|
||||
user: user
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue