From 12ebeef1300b70e44b2aa25dbffeb82df261e26d Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 7 Oct 2019 19:36:10 +0700 Subject: [PATCH 01/11] Add CreateFollowingRelationships migration --- ...7073319_create_following_relationships.exs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 priv/repo/migrations/20191007073319_create_following_relationships.exs diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs new file mode 100644 index 000000000..7b8a2f9bd --- /dev/null +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -0,0 +1,92 @@ +defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do + use Ecto.Migration + + # had to disable these to be able to restore `following` index concurrently + # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently + @disable_ddl_transaction true + @disable_migration_lock true + + def change do + create_if_not_exists table(:following_relationships) do + add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) + add(:following_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) + add(:state, :string, null: false) + + timestamps() + end + + create_if_not_exists(index(:following_relationships, :follower_id)) + create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) + + execute(insert_following_relationships_rows(), restore_following_column()) + + drop(index(:users, [:following], concurrently: true, using: :gin)) + + alter table(:users) do + remove(:following, {:array, :string}, default: []) + end + end + + defp insert_following_relationships_rows do + """ + INSERT INTO + following_relationships ( + follower_id, + following_id, + state, + inserted_at, + updated_at + ) + SELECT + followers.id, + following.id, + activities.data ->> 'state', + (activities.data ->> 'published') :: timestamp, + now() + FROM + activities + JOIN users AS followers ON (activities.actor = followers.ap_id) + JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ON CONFLICT DO NOTHING + """ + end + + defp restore_following_column do + """ + UPDATE + users + SET + following = following_query.following, + updated_at = now() + FROM + ( + SELECT + followers.id AS follower_id, + array_prepend( + followers.follower_address, + array_agg(DISTINCT following.ap_id) FILTER ( + WHERE + following.ap_id IS NOT NULL + ) + ) as following + FROM + users AS followers + LEFT OUTER JOIN activities ON ( + followers.ap_id = activities.actor + AND activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ) + LEFT OUTER JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + followers.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + GROUP BY + followers.id, + followers.ap_id + ) AS following_query + WHERE + following_query.follower_id = users.id; + """ + end +end From 6291eaa5902d5c1565e8969117fdf92c373716b6 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 8 Oct 2019 13:06:48 +0700 Subject: [PATCH 02/11] Update CreateFollowingRelationships --- ...7073319_create_following_relationships.exs | 79 ++++++++++++------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 7b8a2f9bd..fe229240b 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -18,7 +18,8 @@ def change do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) - execute(insert_following_relationships_rows(), restore_following_column()) + execute(import_following_from_users(), "") + execute(import_following_from_activities(), restore_following_column()) drop(index(:users, [:following], concurrently: true, using: :gin)) @@ -27,7 +28,33 @@ def change do end end - defp insert_following_relationships_rows do + defp import_following_from_users do + """ + INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) + SELECT + relations.follower_id, + following.id, + 'accept', + now(), + now() + FROM ( + SELECT + users.id AS follower_id, + unnest(users.following) AS following_ap_id + FROM + users + WHERE + users.following != '{}' + AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + ) AS relations + JOIN users AS "following" ON "following".follower_address = relations.following_ap_id + + WHERE relations.follower_id != following.id + ON CONFLICT DO NOTHING + """ + end + + defp import_following_from_activities do """ INSERT INTO following_relationships ( @@ -49,7 +76,9 @@ defp insert_following_relationships_rows do JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) WHERE activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ON CONFLICT DO NOTHING + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ORDER BY activities.updated_at DESC + ON CONFLICT DO NOTHING """ end @@ -58,35 +87,25 @@ defp restore_following_column do UPDATE users SET - following = following_query.following, + following = following_query.following_array, updated_at = now() - FROM - ( - SELECT - followers.id AS follower_id, - array_prepend( - followers.follower_address, - array_agg(DISTINCT following.ap_id) FILTER ( - WHERE - following.ap_id IS NOT NULL - ) - ) as following - FROM - users AS followers - LEFT OUTER JOIN activities ON ( - followers.ap_id = activities.actor - AND activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - ) - LEFT OUTER JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) - WHERE - followers.email IS NOT NULL -- Exclude `internal/fetch` and `relay` - GROUP BY - followers.id, - followers.ap_id - ) AS following_query + FROM ( + SELECT + follwer.id AS follower_id, + CASE follwer.local + WHEN TRUE THEN + array_prepend(follwer.follower_address, array_agg(following.follower_address)) + ELSE + array_agg(following.follower_address) + END AS following_array + FROM + following_relationships + JOIN users AS follwer ON follwer.id = following_relationships.follower_id + JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + GROUP BY + follwer.id) AS following_query WHERE - following_query.follower_id = users.id; + following_query.follower_id = users.id """ end end From 761ad0b48e552e7b199fe1a624e3d03aa91981a3 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 8 Oct 2019 20:27:42 +0700 Subject: [PATCH 03/11] Split CreateFollowingRelationships to multiple migrations --- ...7073319_create_following_relationships.exs | 92 ------------------- ...132217_migrate_following_relationships.exs | 89 ++++++++++++++++++ .../20191008132427_drop_users_following.exs | 14 +++ 3 files changed, 103 insertions(+), 92 deletions(-) create mode 100644 priv/repo/migrations/20191008132217_migrate_following_relationships.exs create mode 100644 priv/repo/migrations/20191008132427_drop_users_following.exs diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index fe229240b..8ffc870d9 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -3,8 +3,6 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do # had to disable these to be able to restore `following` index concurrently # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently - @disable_ddl_transaction true - @disable_migration_lock true def change do create_if_not_exists table(:following_relationships) do @@ -17,95 +15,5 @@ def change do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) - - execute(import_following_from_users(), "") - execute(import_following_from_activities(), restore_following_column()) - - drop(index(:users, [:following], concurrently: true, using: :gin)) - - alter table(:users) do - remove(:following, {:array, :string}, default: []) - end - end - - defp import_following_from_users do - """ - INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) - SELECT - relations.follower_id, - following.id, - 'accept', - now(), - now() - FROM ( - SELECT - users.id AS follower_id, - unnest(users.following) AS following_ap_id - FROM - users - WHERE - users.following != '{}' - AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` - ) AS relations - JOIN users AS "following" ON "following".follower_address = relations.following_ap_id - - WHERE relations.follower_id != following.id - ON CONFLICT DO NOTHING - """ - end - - defp import_following_from_activities do - """ - INSERT INTO - following_relationships ( - follower_id, - following_id, - state, - inserted_at, - updated_at - ) - SELECT - followers.id, - following.id, - activities.data ->> 'state', - (activities.data ->> 'published') :: timestamp, - now() - FROM - activities - JOIN users AS followers ON (activities.actor = followers.ap_id) - JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) - WHERE - activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - ORDER BY activities.updated_at DESC - ON CONFLICT DO NOTHING - """ - end - - defp restore_following_column do - """ - UPDATE - users - SET - following = following_query.following_array, - updated_at = now() - FROM ( - SELECT - follwer.id AS follower_id, - CASE follwer.local - WHEN TRUE THEN - array_prepend(follwer.follower_address, array_agg(following.follower_address)) - ELSE - array_agg(following.follower_address) - END AS following_array - FROM - following_relationships - JOIN users AS follwer ON follwer.id = following_relationships.follower_id - JOIN users AS FOLLOWING ON following.id = following_relationships.following_id - GROUP BY - follwer.id) AS following_query - WHERE - following_query.follower_id = users.id - """ end end diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs new file mode 100644 index 000000000..7f996f5a5 --- /dev/null +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -0,0 +1,89 @@ +defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do + use Ecto.Migration + + def change do + execute(import_following_from_users(), "") + execute(import_following_from_activities(), restore_following_column()) + end + + defp import_following_from_users do + """ + INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) + SELECT + relations.follower_id, + following.id, + 'accept', + now(), + now() + FROM ( + SELECT + users.id AS follower_id, + unnest(users.following) AS following_ap_id + FROM + users + WHERE + users.following != '{}' + AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + ) AS relations + JOIN users AS "following" ON "following".follower_address = relations.following_ap_id + + WHERE relations.follower_id != following.id + ON CONFLICT DO NOTHING + """ + end + + defp import_following_from_activities do + """ + INSERT INTO + following_relationships ( + follower_id, + following_id, + state, + inserted_at, + updated_at + ) + SELECT + followers.id, + following.id, + activities.data ->> 'state', + (activities.data ->> 'published') :: timestamp, + now() + FROM + activities + JOIN users AS followers ON (activities.actor = followers.ap_id) + JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ORDER BY activities.updated_at DESC + ON CONFLICT DO NOTHING + """ + end + + defp restore_following_column do + """ + UPDATE + users + SET + following = following_query.following_array, + updated_at = now() + FROM ( + SELECT + follwer.id AS follower_id, + CASE follwer.local + WHEN TRUE THEN + array_prepend(follwer.follower_address, array_agg(following.follower_address)) + ELSE + array_agg(following.follower_address) + END AS following_array + FROM + following_relationships + JOIN users AS follwer ON follwer.id = following_relationships.follower_id + JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + GROUP BY + follwer.id) AS following_query + WHERE + following_query.follower_id = users.id + """ + end +end diff --git a/priv/repo/migrations/20191008132427_drop_users_following.exs b/priv/repo/migrations/20191008132427_drop_users_following.exs new file mode 100644 index 000000000..17805db36 --- /dev/null +++ b/priv/repo/migrations/20191008132427_drop_users_following.exs @@ -0,0 +1,14 @@ +defmodule Pleroma.Repo.Migrations.DropUsersFollowing do + use Ecto.Migration + + @disable_ddl_transaction true + @disable_migration_lock true + + def change do + drop(index(:users, [:following], concurrently: true, using: :gin)) + + alter table(:users) do + remove(:following, {:array, :string}, default: []) + end + end +end From 2c7ff32e5b95926af9b6573a6dc6ea96e3ba7dd5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 10 Oct 2019 21:11:34 +0700 Subject: [PATCH 04/11] Add `thread_visibility` to migrations --- ...7073319_create_following_relationships.exs | 136 +++++++++++++++++- .../20191008132427_drop_users_following.exs | 2 + 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 8ffc870d9..7daaf0575 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -1,9 +1,6 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do use Ecto.Migration - # had to disable these to be able to restore `following` index concurrently - # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently - def change do create_if_not_exists table(:following_relationships) do add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) @@ -15,5 +12,138 @@ def change do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) + + execute(update_thread_visibility(), restore_thread_visibility()) + end + + # The only difference with the original verion: `actor_user` replaced with `actor_user_following` + def update_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + actor_user_following varchar[]; + BEGIN + --- Fetch actor following + SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships + JOIN users ON users.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id + WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + IF ARRAY[author_fa] && actor_user_following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ + end + + # priv/repo/migrations/20190515222404_add_thread_visibility_function.exs + def restore_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + actor_user users%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + BEGIN + --- Fetch our actor. + SELECT * INTO actor_user FROM users WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + IF ARRAY[author_fa] && actor_user.following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ end end diff --git a/priv/repo/migrations/20191008132427_drop_users_following.exs b/priv/repo/migrations/20191008132427_drop_users_following.exs index 17805db36..21c0af9f4 100644 --- a/priv/repo/migrations/20191008132427_drop_users_following.exs +++ b/priv/repo/migrations/20191008132427_drop_users_following.exs @@ -1,6 +1,8 @@ defmodule Pleroma.Repo.Migrations.DropUsersFollowing do use Ecto.Migration + # had to disable these to be able to restore `following` index concurrently + # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently @disable_ddl_transaction true @disable_migration_lock true From 059005ff829c0313c62ddf5fbcd95f8892920228 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 11 Oct 2019 02:35:32 +0700 Subject: [PATCH 05/11] Replace `user.following` with Pleroma.FollowingRelationship --- lib/mix/tasks/pleroma/database.ex | 6 +- lib/mix/tasks/pleroma/relay.ex | 5 +- lib/mix/tasks/pleroma/user.ex | 2 +- lib/pleroma/bbs/handler.ex | 2 +- lib/pleroma/following_relationship.ex | 110 ++++++++++++++++++ lib/pleroma/user.ex | 99 ++++------------ lib/pleroma/user/query.ex | 34 +++++- lib/pleroma/web/activity_pub/activity_pub.ex | 6 +- .../activity_pub/activity_pub_controller.ex | 4 +- .../web/activity_pub/transmogrifier.ex | 63 ++++------ lib/pleroma/web/activity_pub/visibility.ex | 2 +- lib/pleroma/web/common_api/common_api.ex | 3 + .../controllers/timeline_controller.ex | 8 +- .../controllers/account_controller.ex | 2 +- ...7073319_create_following_relationships.exs | 2 +- test/support/factory.ex | 3 +- test/tasks/database_test.exs | 8 +- test/tasks/relay_test.exs | 24 ++-- test/tasks/user_test.exs | 5 +- test/user_test.exs | 81 +++++-------- test/web/activity_pub/activity_pub_test.exs | 6 +- test/web/activity_pub/relay_test.exs | 5 +- test/web/activity_pub/transmogrifier_test.exs | 7 +- test/web/activity_pub/visibilty_test.exs | 3 +- .../controllers/account_controller_test.exs | 2 +- .../follow_request_controller_test.exs | 5 +- test/web/streamer/streamer_test.exs | 9 +- test/web/twitter_api/util_controller_test.exs | 4 +- 28 files changed, 275 insertions(+), 235 deletions(-) create mode 100644 lib/pleroma/following_relationship.ex diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index cfd9eeada..72b706e1a 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -52,9 +52,9 @@ def run(["bump_all_conversations"]) do def run(["update_users_following_followers_counts"]) do start_pleroma() - users = Repo.all(User) - Enum.each(users, &User.remove_duplicated_following/1) - Enum.each(users, &User.update_follower_count/1) + User + |> Repo.all() + |> Enum.each(&User.update_follower_count/1) end def run(["prune_objects" | args]) do diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index d7a7b599f..eafddada6 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -36,8 +36,9 @@ def run(["unfollow", target]) do def run(["list"]) do start_pleroma() - with %User{following: following} = _user <- Relay.get_actor() do - following + with %User{} = user <- Relay.get_actor() do + user + |> User.following() |> Enum.map(fn entry -> URI.parse(entry).host end) |> Enum.uniq() |> Enum.each(&shell_info(&1)) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 134b5bccc..8866afdf6 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -162,7 +162,7 @@ def run(["unsubscribe", nickname]) do user = User.get_cached_by_id(user.id) - if Enum.empty?(user.following) do + if Enum.empty?(User.get_friends(user)) do shell_info("Successfully unsubscribed all followers from #{user.nickname}") end else diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index fa838a4e4..b0e9ebbd0 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -97,7 +97,7 @@ def handle_command(state, "home") do |> Map.put("user", user) activities = - [user.ap_id | user.following] + [user.ap_id | Pleroma.User.following(user)] |> ActivityPub.fetch_activities(params) Enum.each(activities, fn activity -> diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex new file mode 100644 index 000000000..0d789b5a6 --- /dev/null +++ b/lib/pleroma/following_relationship.ex @@ -0,0 +1,110 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FollowingRelationship do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias FlakeId.Ecto.CompatType + alias Pleroma.Repo + alias Pleroma.User + + schema "following_relationships" do + field(:state, :string, default: "accept") + + belongs_to(:follower, User, type: CompatType) + belongs_to(:following, User, type: CompatType) + + timestamps() + end + + def changeset(%__MODULE__{} = following_relationship, attrs) do + following_relationship + |> cast(attrs, [:state]) + |> put_assoc(:follower, attrs.follower) + |> put_assoc(:following, attrs.following) + |> validate_required([:state, :follower, :following]) + end + + def get(%User{} = follower, %User{} = following) do + __MODULE__ + |> where(follower_id: ^follower.id, following_id: ^following.id) + |> Repo.one() + end + + def update(follower, following, "reject"), do: unfollow(follower, following) + + def update(%User{} = follower, %User{} = following, state) do + case get(follower, following) do + nil -> + follow(follower, following, state) + + following_relationship -> + following_relationship + |> cast(%{state: state}, [:state]) + |> validate_required([:state]) + |> Repo.update() + end + end + + def follow(%User{} = follower, %User{} = following, state \\ "accept") do + %__MODULE__{} + |> changeset(%{follower: follower, following: following, state: state}) + |> Repo.insert(on_conflict: :nothing) + end + + def unfollow(%User{} = follower, %User{} = following) do + case get(follower, following) do + nil -> {:ok, nil} + %__MODULE__{} = following_relationship -> Repo.delete(following_relationship) + end + end + + def follower_count(%User{} = user) do + %{followers: user, deactivated: false} + |> User.Query.build() + |> Repo.aggregate(:count, :id) + end + + def following_count(%User{id: nil}), do: 0 + + def following_count(%User{} = user) do + %{friends: user, deactivated: false} + |> User.Query.build() + |> Repo.aggregate(:count, :id) + end + + def get_follow_requests(%User{id: id}) do + __MODULE__ + |> join(:inner, [r], f in assoc(r, :follower)) + |> where([r], r.state == "pending") + |> where([r], r.following_id == ^id) + |> select([r, f], f) + |> Repo.all() + end + + def following?(%User{id: follower_id}, %User{id: followed_id}) do + __MODULE__ + |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") + |> Repo.exists?() + end + + def following(%User{} = user) do + following = + __MODULE__ + |> join(:inner, [r], u in User, on: r.following_id == u.id) + |> where([r], r.follower_id == ^user.id) + |> where([r], r.state == "accept") + |> select([r, u], u.follower_address) + |> Repo.all() + + if user.nickname in [nil, "internal.fetch"] do + following + else + [user.follower_address | following] + end + end +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2cfb13a8c..c32f6b429 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -13,6 +13,7 @@ defmodule Pleroma.User do alias Pleroma.Activity alias Pleroma.Conversation.Participation alias Pleroma.Delivery + alias Pleroma.FollowingRelationship alias Pleroma.Keys alias Pleroma.Notification alias Pleroma.Object @@ -52,7 +53,6 @@ defmodule Pleroma.User do field(:password, :string, virtual: true) field(:password_confirmation, :string, virtual: true) field(:keys, :string) - field(:following, {:array, :string}, default: []) field(:ap_id, :string) field(:avatar, :map) field(:local, :boolean, default: true) @@ -162,13 +162,7 @@ def restrict_deactivated(query) do ) end - def following_count(%User{following: []}), do: 0 - - def following_count(%User{} = user) do - user - |> get_friends_query() - |> Repo.aggregate(:count, :id) - end + defdelegate following_count(user), to: FollowingRelationship defp truncate_if_exists(params, key, max_length) do if Map.has_key?(params, key) and is_binary(params[key]) do @@ -216,7 +210,7 @@ def update_changeset(struct, params \\ %{}) do name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) struct - |> cast(params, [:bio, :name, :avatar, :following]) + |> cast(params, [:bio, :name, :avatar]) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) @@ -324,7 +318,6 @@ defp put_following_and_follower_address(changeset) do followers = ap_followers(%User{nickname: get_field(changeset, :nickname)}) changeset - |> put_change(:following, [followers]) |> put_change(:follower_address, followers) end @@ -378,8 +371,11 @@ def needs_update?(%User{local: false} = user) do def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} - def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do - {:ok, follower} + def maybe_direct_follow( + %User{} = follower, + %User{local: true, info: %{locked: true}} = followed + ) do + follow(follower, followed, "pending") end def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do @@ -397,37 +393,22 @@ def maybe_direct_follow(%User{} = follower, %User{} = followed) do @doc "A mass follow for local users. Respects blocks in both directions but does not create activities." @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()} def follow_all(follower, followeds) do - followed_addresses = - followeds - |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) - |> Enum.map(fn %{follower_address: fa} -> fa end) + followeds = + Enum.reject(followeds, fn followed -> + blocks?(follower, followed) || blocks?(followed, follower) + end) - q = - from(u in User, - where: u.id == ^follower.id, - update: [ - set: [ - following: - fragment( - "array(select distinct unnest (array_cat(?, ?)))", - u.following, - ^followed_addresses - ) - ] - ], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + Enum.each(followeds, &follow(follower, &1, "accept")) Enum.each(followeds, &update_follower_count/1) set_cache(follower) end - def follow(%User{} = follower, %User{info: info} = followed) do + defdelegate following(user), to: FollowingRelationship + + def follow(%User{} = follower, %User{info: info} = followed, state \\ "accept") do deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) - ap_followers = followed.follower_address cond do info.deactivated -> @@ -441,14 +422,7 @@ def follow(%User{} = follower, %User{info: info} = followed) do Websub.subscribe(follower, followed) end - q = - from(u in User, - where: u.id == ^follower.id, - update: [push: [following: ^ap_followers]], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + FollowingRelationship.follow(follower, followed, state) follower = maybe_update_following_count(follower) @@ -459,17 +433,8 @@ def follow(%User{} = follower, %User{info: info} = followed) do end def unfollow(%User{} = follower, %User{} = followed) do - ap_followers = followed.follower_address - if following?(follower, followed) and follower.ap_id != followed.ap_id do - q = - from(u in User, - where: u.id == ^follower.id, - update: [pull: [following: ^ap_followers]], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + FollowingRelationship.unfollow(follower, followed) follower = maybe_update_following_count(follower) @@ -483,10 +448,7 @@ def unfollow(%User{} = follower, %User{} = followed) do end end - @spec following?(User.t(), User.t()) :: boolean - def following?(%User{} = follower, %User{} = followed) do - Enum.member?(follower.following, followed.follower_address) - end + defdelegate following?(follower, followed), to: FollowingRelationship def locked?(%User{} = user) do user.info.locked || false @@ -707,16 +669,7 @@ def get_friends_ids(user, page \\ nil) do |> Repo.all() end - @spec get_follow_requests(User.t()) :: {:ok, [User.t()]} - def get_follow_requests(%User{} = user) do - user - |> Activity.follow_requests_for_actor() - |> join(:inner, [a], u in User, on: a.actor == u.ap_id) - |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) - |> group_by([a, u], u.id) - |> select([a, u], u) - |> Repo.all() - end + defdelegate get_follow_requests(user), to: FollowingRelationship def increase_note_count(%User{} = user) do User @@ -899,18 +852,6 @@ def increment_unread_conversation_count(conversation, %User{local: true} = user) def increment_unread_conversation_count(_, _), do: :noop - def remove_duplicated_following(%User{following: following} = user) do - uniq_following = Enum.uniq(following) - - if length(following) == length(uniq_following) do - {:ok, user} - else - user - |> update_changeset(%{following: uniq_following}) - |> update_and_set_cache() - end - end - @spec get_users_from_set([String.t()], boolean()) :: [User.t()] def get_users_from_set(ap_ids, local_only \\ true) do criteria = %{ap_id: ap_ids, deactivated: false} diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 2baf016cf..f32def4f5 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -28,6 +28,8 @@ defmodule Pleroma.User.Query do """ import Ecto.Query import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1] + + alias Pleroma.FollowingRelationship alias Pleroma.User @type criteria :: @@ -130,18 +132,40 @@ defp compose_query({:deactivated, true}, query) do |> where([u], not is_nil(u.nickname)) end - defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do - where(query, [u], fragment("? <@ ?", ^[follower_address], u.following)) + defp compose_query({:followers, %User{id: id}}, query) do + query |> where([u], u.id != ^id) + |> join(:inner, [u], r in FollowingRelationship, + as: :relationships, + on: r.following_id == ^id and r.follower_id == u.id + ) + |> where([relationships: r], r.state == "accept") end - defp compose_query({:friends, %User{id: id, following: following}}, query) do - where(query, [u], u.follower_address in ^following) + defp compose_query({:friends, %User{id: id}}, query) do + query |> where([u], u.id != ^id) + |> join(:inner, [u], r in FollowingRelationship, + as: :relationships, + on: r.following_id == u.id and r.follower_id == ^id + ) + |> where([relationships: r], r.state == "accept") end defp compose_query({:recipients_from_activity, to}, query) do - where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to)) + query + |> join(:left, [u], r in FollowingRelationship, + as: :relationships, + on: r.follower_id == u.id + ) + |> join(:left, [relationships: r], f in User, + as: :following, + on: f.id == r.following_id + ) + |> where( + [u, following: f, relationships: r], + u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") + ) end defp compose_query({:order_by, key}, query) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9f29087df..db0855329 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -501,7 +501,9 @@ defp fetch_activities_for_context_query(context, opts) do public = [Pleroma.Constants.as_public()] recipients = - if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public + if opts["user"], + do: [opts["user"].ap_id | User.following(opts["user"])] ++ public, + else: public from(activity in Activity) |> maybe_preload_objects(opts) @@ -652,7 +654,7 @@ defp user_activities_recipients(%{"godmode" => true}) do defp user_activities_recipients(%{"reading_user" => reading_user}) do if reading_user do - [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following] + [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)] else [Pleroma.Constants.as_public()] end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 080030eb5..9010eab91 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -319,12 +319,12 @@ def read_inbox( when page? in [true, "true"] do activities = if params["max_id"] do - ActivityPub.fetch_activities([user.ap_id | user.following], %{ + ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{ "max_id" => params["max_id"], "limit" => 10 }) else - ActivityPub.fetch_activities([user.ap_id | user.following], %{"limit" => 10}) + ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{"limit" => 10}) end conn diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 872ed0eb2..54ba49520 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do A module to handle coding from internal to wire ActivityPub and back. """ alias Pleroma.Activity + alias Pleroma.FollowingRelationship alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.Repo @@ -474,7 +475,8 @@ def handle_incoming( {_, false} <- {:user_locked, User.locked?(followed)}, {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)}, {_, {:ok, _}} <- - {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do + {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do ActivityPub.accept(%{ to: [follower.ap_id], actor: followed, @@ -484,6 +486,7 @@ def handle_incoming( else {:user_blocked, true} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") ActivityPub.reject(%{ to: [follower.ap_id], @@ -494,6 +497,7 @@ def handle_incoming( {:follow, {:error, _}} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") ActivityPub.reject(%{ to: [follower.ap_id], @@ -522,7 +526,7 @@ def handle_incoming( {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _follower} = User.follow(follower, followed) do + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do ActivityPub.accept(%{ to: follow_activity.data["to"], type: "Accept", @@ -544,6 +548,7 @@ def handle_incoming( {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, activity} <- ActivityPub.reject(%{ to: follow_activity.data["to"], @@ -552,8 +557,6 @@ def handle_incoming( object: follow_activity.data["id"], local: false }) do - User.unfollow(follower, followed) - {:ok, activity} else _e -> :error @@ -1050,46 +1053,24 @@ defp strip_internal_tags(object), do: object def perform(:user_upgrade, user) do # we pass a fake user so that the followers collection is stripped away old_follower_address = User.ap_followers(%User{nickname: user.nickname}) - - q = - from( - u in User, - where: ^old_follower_address in u.following, - update: [ - set: [ - following: - fragment( - "array_replace(?,?,?)", - u.following, - ^old_follower_address, - ^user.follower_address - ) - ] - ] - ) - - Repo.update_all(q, []) - maybe_retire_websub(user.ap_id) - q = - from( - a in Activity, - where: ^old_follower_address in a.recipients, - update: [ - set: [ - recipients: - fragment( - "array_replace(?,?,?)", - a.recipients, - ^old_follower_address, - ^user.follower_address - ) - ] + from( + a in Activity, + where: ^old_follower_address in a.recipients, + update: [ + set: [ + recipients: + fragment( + "array_replace(?,?,?)", + a.recipients, + ^old_follower_address, + ^user.follower_address + ) ] - ) - - Repo.update_all(q, []) + ] + ) + |> Repo.update_all([]) end def upgrade_user_from_ap_id(ap_id) do diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 270d0fa02..47bc4e1ca 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -58,7 +58,7 @@ def visible_for_user?(activity, nil) do end def visible_for_user?(activity, user) do - x = [user.ap_id | user.following] + x = [user.ap_id | User.following(user)] y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || []) visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 386408d51..40b3930fb 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.ActivityExpiration alias Pleroma.Conversation.Participation + alias Pleroma.FollowingRelationship alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -40,6 +41,7 @@ def accept_follow_request(follower, followed) do with {:ok, follower} <- User.follow(follower, followed), %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), {:ok, _activity} <- ActivityPub.accept(%{ to: [follower.ap_id], @@ -54,6 +56,7 @@ def accept_follow_request(follower, followed) do def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _activity} <- ActivityPub.reject(%{ to: [follower.ap_id], diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 9f086a8c2..f2d2d3ccb 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.Pagination alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) @@ -28,7 +29,7 @@ def home(%{assigns: %{user: user}} = conn, params) do |> Map.put("muting_user", user) |> Map.put("user", user) - recipients = [user.ap_id | user.following] + recipients = [user.ap_id | User.following(user)] activities = recipients @@ -128,9 +129,12 @@ def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do # we must filter the following list for the user to avoid leaking statuses the user # does not actually have permission to see (for more info, peruse security issue #270). + + user_following = User.following(user) + activities = following - |> Enum.filter(fn x -> x in user.following end) + |> Enum.filter(fn x -> x in user_following end) |> ActivityPub.fetch_activities_bounded(following, params) |> Enum.reverse() diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 9012e2175..7ce9d5da2 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -132,7 +132,7 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do recipients = if for_user do - [Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following] + [Pleroma.Constants.as_public()] ++ [for_user.ap_id | User.following(for_user)] else [Pleroma.Constants.as_public()] end diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 7daaf0575..d49e24ee4 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -16,7 +16,7 @@ def change do execute(update_thread_visibility(), restore_thread_visibility()) end - # The only difference with the original verion: `actor_user` replaced with `actor_user_following` + # The only difference between the original version: `actor_user` replaced with `actor_user_following` def update_thread_visibility do """ CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ diff --git a/test/support/factory.ex b/test/support/factory.ex index b180844cd..74f292a1d 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -39,8 +39,7 @@ def user_factory do user | ap_id: User.ap_id(user), follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] + following_address: User.ap_following(user) } end diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs index b63dcac00..bf5ba7883 100644 --- a/test/tasks/database_test.exs +++ b/test/tasks/database_test.exs @@ -72,25 +72,25 @@ test "it prunes old objects from the database" do describe "running update_users_following_followers_counts" do test "following and followers count are updated" do [user, user2] = insert_pair(:user) - {:ok, %User{following: following, info: info} = user} = User.follow(user, user2) + {:ok, %User{info: info} = user} = User.follow(user, user2) + + following = User.following(user) assert length(following) == 2 assert info.follower_count == 0 {:ok, user} = user - |> Ecto.Changeset.change(%{following: following ++ following}) |> User.change_info(&Ecto.Changeset.change(&1, %{follower_count: 3})) |> Repo.update() - assert length(user.following) == 4 assert user.info.follower_count == 3 assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) user = User.get_by_id(user.id) - assert length(user.following) == 2 + assert length(User.following(user)) == 2 assert user.info.follower_count == 0 end end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index c866608ab..04a1e45d7 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -51,7 +51,7 @@ test "relay is unfollowed" do target_user = User.get_cached_by_ap_id(target_instance) follow_activity = Utils.fetch_latest_follow(local_user, target_user) User.follow(local_user, target_user) - assert "#{target_instance}/followers" in refresh_record(local_user).following + assert "#{target_instance}/followers" in User.following(local_user) Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) @@ -68,7 +68,7 @@ test "relay is unfollowed" do assert undo_activity.data["type"] == "Undo" assert undo_activity.data["actor"] == local_user.ap_id assert undo_activity.data["object"] == cancelled_activity.data - refute "#{target_instance}/followers" in refresh_record(local_user).following + refute "#{target_instance}/followers" in User.following(local_user) end end @@ -78,20 +78,18 @@ test "Prints relay subscription list" do refute_receive {:mix_shell, :info, _} - Pleroma.Web.ActivityPub.Relay.get_actor() - |> Ecto.Changeset.change( - following: [ - "http://test-app.com/user/test1", - "http://test-app.com/user/test1", - "http://test-app-42.com/user/test1" - ] - ) - |> Pleroma.User.update_and_set_cache() + relay_user = Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["test-app.com"]} - assert_receive {:mix_shell, :info, ["test-app-42.com"]} + assert_receive {:mix_shell, :info, ["mstdn.io"]} + assert_receive {:mix_shell, :info, ["mastodon.example.org"]} end end end diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index cf12d9ed6..c0e4ba85c 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -139,7 +139,8 @@ test "no user to toggle" do describe "running unsubscribe" do test "user is unsubscribed" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + User.follow(user, followed, "accept") Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) @@ -154,7 +155,7 @@ test "user is unsubscribed" do assert message =~ "Successfully unsubscribed" user = User.get_cached_by_nickname(user.nickname) - assert Enum.empty?(user.following) + assert Enum.empty?(User.get_friends(user)) assert user.info.deactivated end diff --git a/test/user_test.exs b/test/user_test.exs index 019e7b400..85e55876d 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -88,10 +88,9 @@ test "doesn't return already accepted or duplicate follow requests" do CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked) CommonAPI.follow(accepted_follower, locked) - User.follow(accepted_follower, locked) + Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept") - assert [activity] = User.get_follow_requests(locked) - assert activity + assert [^pending_follower] = User.get_follow_requests(locked) end test "clears follow requests when requester is blocked" do @@ -136,10 +135,10 @@ test "follow_all follows mutliple users without duplicating" do followed_two = insert(:user) {:ok, user} = User.follow_all(user, [followed_zero, followed_one]) - assert length(user.following) == 3 + assert length(User.following(user)) == 3 {:ok, user} = User.follow_all(user, [followed_one, followed_two]) - assert length(user.following) == 4 + assert length(User.following(user)) == 4 end test "follow takes a user and another user" do @@ -153,7 +152,7 @@ test "follow takes a user and another user" do followed = User.get_cached_by_ap_id(followed.ap_id) assert followed.info.follower_count == 1 - assert User.ap_followers(followed) in user.following + assert User.ap_followers(followed) in User.following(user) end test "can't follow a deactivated users" do @@ -198,7 +197,7 @@ test "local users do not automatically follow local locked accounts" do # assert followed.local == false # {:ok, user} = User.follow(user, followed) - # assert User.ap_followers(followed) in user.following + # assert User.ap_followers(followed) in User.following(user) # query = from w in WebsubClientSubscription, # where: w.topic == ^followed.info["topic"] @@ -235,26 +234,29 @@ test "unfollow with syncronizes external user" do nickname: "fuser2", ap_id: "http://localhost:4001/users/fuser2", follower_address: "http://localhost:4001/users/fuser2/followers", - following_address: "http://localhost:4001/users/fuser2/following", - following: [User.ap_followers(followed)] + following_address: "http://localhost:4001/users/fuser2/following" }) + {:ok, user} = User.follow(user, followed, "accept") + {:ok, user, _activity} = User.unfollow(user, followed) user = User.get_cached_by_id(user.id) - assert user.following == [] + assert User.following(user) == [user.follower_address] end test "unfollow takes a user and another user" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + + {:ok, user} = User.follow(user, followed, "accept") + + assert User.following(user) == [user.follower_address, followed.follower_address] {:ok, user, _activity} = User.unfollow(user, followed) - user = User.get_cached_by_id(user.id) - - assert user.following == [] + assert User.following(user) == [user.follower_address] end test "unfollow doesn't unfollow yourself" do @@ -262,14 +264,14 @@ test "unfollow doesn't unfollow yourself" do {:error, _} = User.unfollow(user, user) - user = User.get_cached_by_id(user.id) - assert user.following == [user.ap_id] + assert User.following(user) == [user.follower_address] end end test "test if a user is following another user" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + User.follow(user, followed, "accept") assert User.following?(user, followed) refute User.following?(followed, user) @@ -352,7 +354,7 @@ test "it restricts certain nicknames" do refute changeset.valid? end - test "it sets the password_hash, ap_id and following fields" do + test "it sets the password_hash and ap_id" do changeset = User.register_changeset(%User{}, @full_user_data) assert changeset.valid? @@ -360,10 +362,6 @@ test "it sets the password_hash, ap_id and following fields" do assert is_binary(changeset.changes[:password_hash]) assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) - assert changeset.changes[:following] == [ - User.ap_followers(%User{nickname: @full_user_data.nickname}) - ] - assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" end @@ -671,37 +669,6 @@ test "it sets the info->follower_count property" do end end - describe "remove duplicates from following list" do - test "it removes duplicates" do - user = insert(:user) - follower = insert(:user) - - {:ok, %User{following: following} = follower} = User.follow(follower, user) - assert length(following) == 2 - - {:ok, follower} = - follower - |> User.update_changeset(%{following: following ++ following}) - |> Repo.update() - - assert length(follower.following) == 4 - - {:ok, follower} = User.remove_duplicated_following(follower) - assert length(follower.following) == 2 - end - - test "it does nothing when following is uniq" do - user = insert(:user) - follower = insert(:user) - - {:ok, follower} = User.follow(follower, user) - assert length(follower.following) == 2 - - {:ok, follower} = User.remove_duplicated_following(follower) - assert length(follower.following) == 2 - end - end - describe "follow_import" do test "it imports user followings from list" do [user1, user2, user3] = insert_list(3, :user) @@ -1010,7 +977,9 @@ test "hide a user's statuses from timelines and notifications" do assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark) assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] == - ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2}) + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + "user" => user2 + }) {:ok, _user} = User.deactivate(user) @@ -1018,7 +987,9 @@ test "hide a user's statuses from timelines and notifications" do assert [] == Pleroma.Notification.for_user(user2) assert [] == - ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2}) + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + "user" => user2 + }) end end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index c9f2a92e7..75e928a14 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -606,7 +606,7 @@ test "does include announces on request" do {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster) - [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following]) + [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)]) assert announce_activity.id == announce.id end @@ -1132,7 +1132,7 @@ test "it filters broken threads" do }) activities = - ActivityPub.fetch_activities([user1.ap_id | user1.following]) + ActivityPub.fetch_activities([user1.ap_id | User.following(user1)]) |> Enum.map(fn a -> a.id end) private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"]) @@ -1142,7 +1142,7 @@ test "it filters broken threads" do assert length(activities) == 3 activities = - ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1}) + ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1}) |> Enum.map(fn a -> a.id end) assert [public_activity.id, private_activity_1.id] == activities diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 0f7556538..e270cd4c3 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay @@ -50,14 +51,14 @@ test "returns activity" do service_actor = Relay.get_actor() ActivityPub.follow(service_actor, user) Pleroma.User.follow(service_actor, user) - assert "#{user.ap_id}/followers" in refresh_record(service_actor).following + assert "#{user.ap_id}/followers" in User.following(service_actor) assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" assert user.ap_id in activity.recipients assert activity.data["type"] == "Undo" assert activity.data["actor"] == service_actor.ap_id assert activity.data["to"] == [user.ap_id] - refute "#{user.ap_id}/followers" in refresh_record(service_actor).following + refute "#{user.ap_id}/followers" in User.following(service_actor) end end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 50c0bfb84..d96dcd2a0 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1312,7 +1312,8 @@ test "it upgrades a user to activitypub" do follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) }) - user_two = insert(:user, %{following: [user.follower_address]}) + user_two = insert(:user) + Pleroma.FollowingRelationship.follow(user_two, user, "accept") {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) @@ -1359,8 +1360,8 @@ test "it upgrades a user to activitypub" do refute user.follower_address in unrelated_activity.recipients user_two = User.get_cached_by_id(user_two.id) - assert user.follower_address in user_two.following - refute "..." in user_two.following + assert User.following?(user_two, user) + refute "..." in User.following(user_two) end end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs index b62a89e68..4c2e0d207 100644 --- a/test/web/activity_pub/visibilty_test.exs +++ b/test/web/activity_pub/visibilty_test.exs @@ -212,7 +212,8 @@ test "returns false when invalid recipients", %{user: user} do test "returns true if user following to author" do author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + Pleroma.User.follow(user, author) activity = insert(:note_activity, diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 6a59c3d94..a398ef76a 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -457,7 +457,7 @@ test "following without reblogs" do conn = build_conn() - |> assign(:user, follower) + |> assign(:user, User.get_cached_by_id(follower.id)) |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") assert %{"showing_reblogs" => true} = json_response(conn, 200) diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index 4bf292df5..89b676201 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -16,9 +16,7 @@ test "/api/v1/follow_requests works" do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) + {:ok, other_user} = User.follow(other_user, user, "pending") assert User.following?(other_user, user) == false @@ -36,6 +34,7 @@ test "/api/v1/follow_requests/:id/authorize works" do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, other_user} = User.follow(other_user, user, "pending") user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index d33eb1e42..c674e71f5 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -169,7 +169,8 @@ test "it sends to public" do test "it doesn't send to user if recipients invalid and thread containment is enabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + User.follow(user, author, "accept") activity = insert(:note_activity, @@ -191,7 +192,8 @@ test "it doesn't send to user if recipients invalid and thread containment is en test "it sends message if recipients invalid and thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], true) author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + User.follow(user, author, "accept") activity = insert(:note_activity, @@ -213,7 +215,8 @@ test "it sends message if recipients invalid and thread containment is disabled" test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) - user = insert(:user, following: [author.ap_id], info: %{skip_thread_containment: true}) + user = insert(:user, info: %{skip_thread_containment: true}) + User.follow(user, author, "accept") activity = insert(:note_activity, diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 9d4cb70f0..5234a5271 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -366,7 +366,7 @@ test "follows user", %{conn: conn} do |> response(200) assert response =~ "Account followed!" - assert user2.follower_address in refresh_record(user).following + assert user2.follower_address in User.following(user) end test "returns error when user is deactivated", %{conn: conn} do @@ -438,7 +438,7 @@ test "follows", %{conn: conn} do |> response(200) assert response =~ "Account followed!" - assert user2.follower_address in refresh_record(user).following + assert user2.follower_address in User.following(user) end test "returns error when followee not found", %{conn: conn} do From 1d46944fbd17d194d744230cd519d1410e821a47 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 14 Oct 2019 13:50:43 +0700 Subject: [PATCH 06/11] Do not add `follower_address` to `following` for non local users --- lib/pleroma/following_relationship.ex | 2 +- test/user_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 0d789b5a6..2ffac17ee 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -101,7 +101,7 @@ def following(%User{} = user) do |> select([r, u], u.follower_address) |> Repo.all() - if user.nickname in [nil, "internal.fetch"] do + if not user.local or user.nickname in [nil, "internal.fetch"] do following else [user.follower_address | following] diff --git a/test/user_test.exs b/test/user_test.exs index 85e55876d..eb1cf4037 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -243,7 +243,7 @@ test "unfollow with syncronizes external user" do user = User.get_cached_by_id(user.id) - assert User.following(user) == [user.follower_address] + assert User.following(user) == [] end test "unfollow takes a user and another user" do From 8ad015ef64e0d2a4cd9f2979ff08d28be3a635e5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 14 Oct 2019 14:16:57 +0700 Subject: [PATCH 07/11] Skip deactivated users in followers import --- .../20191008132217_migrate_following_relationships.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index 7f996f5a5..c9bc890aa 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -55,6 +55,7 @@ defp import_following_from_activities do WHERE activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + AND NOT (followers.info ? 'deactivated' AND followers.info -> 'deactivated' @> 'true') ORDER BY activities.updated_at DESC ON CONFLICT DO NOTHING """ From c6fba62666702013587e0b60723b9dfe60d1c710 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:19:31 +0700 Subject: [PATCH 08/11] Fix Relay --- lib/pleroma/web/activity_pub/relay.ex | 5 +++-- .../admin_api/admin_api_controller_test.exs | 18 ++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 03fc434a9..830d1cde8 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -53,9 +53,10 @@ def publish(_), do: {:error, "Not implemented"} @spec list() :: {:ok, [String.t()]} | {:error, any()} def list do - with %User{following: following} = _user <- get_actor() do + with %User{} = user <- get_actor() do list = - following + user + |> User.following() |> Enum.map(fn entry -> URI.parse(entry).host end) |> Enum.uniq() diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 9da4940be..6dc0d4dca 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2572,22 +2572,20 @@ test "POST /relay", %{admin: admin} do end test "GET /relay", %{admin: admin} do - Pleroma.Web.ActivityPub.Relay.get_actor() - |> Ecto.Changeset.change( - following: [ - "http://test-app.com/user/test1", - "http://test-app.com/user/test1", - "http://test-app-42.com/user/test1" - ] - ) - |> Pleroma.User.update_and_set_cache() + relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) conn = build_conn() |> assign(:user, admin) |> get("/api/pleroma/admin/relay") - assert json_response(conn, 200)["relays"] -- ["test-app.com", "test-app-42.com"] == [] + assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == [] end test "DELETE /relay", %{admin: admin} do From e37d4b2ddf7928437618d3eb7b5da678e39927cf Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:52:52 +0700 Subject: [PATCH 09/11] Revert "Skip deactivated users in followers import" This reverts commit 8ad015ef64e0d2a4cd9f2979ff08d28be3a635e5. --- .../20191008132217_migrate_following_relationships.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index c9bc890aa..7f996f5a5 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -55,7 +55,6 @@ defp import_following_from_activities do WHERE activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - AND NOT (followers.info ? 'deactivated' AND followers.info -> 'deactivated' @> 'true') ORDER BY activities.updated_at DESC ON CONFLICT DO NOTHING """ From f726d953d59b8c4c2099a3c583e7226b99324bb9 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:56:39 +0700 Subject: [PATCH 10/11] Fix typos --- ...0191008132217_migrate_following_relationships.exs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index 7f996f5a5..9d5c2648f 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -69,19 +69,19 @@ defp restore_following_column do updated_at = now() FROM ( SELECT - follwer.id AS follower_id, - CASE follwer.local + follower.id AS follower_id, + CASE follower.local WHEN TRUE THEN - array_prepend(follwer.follower_address, array_agg(following.follower_address)) + array_prepend(follower.follower_address, array_agg(following.follower_address)) ELSE array_agg(following.follower_address) END AS following_array FROM following_relationships - JOIN users AS follwer ON follwer.id = following_relationships.follower_id - JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + JOIN users AS follower ON follower.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id GROUP BY - follwer.id) AS following_query + follower.id) AS following_query WHERE following_query.follower_id = users.id """ From 478f0883eb994c639923b7b27259a02bcef8708c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 15:55:28 +0700 Subject: [PATCH 11/11] Fix benchmarks --- benchmarks/load_testing/fetcher.ex | 6 ++++-- benchmarks/load_testing/generator.ex | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex index e378c51e7..cdc073b2e 100644 --- a/benchmarks/load_testing/fetcher.ex +++ b/benchmarks/load_testing/fetcher.ex @@ -39,10 +39,12 @@ def query_timelines(user) do "muting_user" => user } + following = User.following(user) + Benchee.run(%{ "User home timeline" => fn -> Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], + following, home_timeline_params ) end, @@ -60,7 +62,7 @@ def query_timelines(user) do home_activities = Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], + following, home_timeline_params ) diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex index 5c5a5c122..b4432bdb7 100644 --- a/benchmarks/load_testing/generator.ex +++ b/benchmarks/load_testing/generator.ex @@ -45,15 +45,13 @@ defp generate_user_data(i) do %{ ap_id: ap_id, follower_address: ap_id <> "/followers", - following_address: ap_id <> "/following", - following: [ap_id] + following_address: ap_id <> "/following" } else %{ ap_id: User.ap_id(user), follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] + following_address: User.ap_following(user) } end