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 --- ...191007073319_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 --- ...191007073319_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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 --- ...191007073319_create_following_relationships.exs | 92 ---------------------- ...91008132217_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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 --- ...191007073319_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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 +- .../web/activity_pub/activity_pub_controller.ex | 4 +- lib/pleroma/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 +- .../pleroma_api/controllers/account_controller.ex | 2 +- ...191007073319_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 +- .../controllers/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 @@ defmodule Mix.Tasks.Pleroma.Database 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 @@ defmodule Mix.Tasks.Pleroma.Relay 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 @@ defmodule Mix.Tasks.Pleroma.User 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 @@ defmodule Pleroma.BBS.Handler 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User 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 @@ defmodule Pleroma.User do 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 @@ defmodule Pleroma.User.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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do 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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {_, 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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do 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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {: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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {: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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {: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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object: follow_activity.data["id"], local: false }) do - User.unfollow(follower, followed) - {:ok, activity} else _e -> :error @@ -1050,46 +1053,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do 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 @@ defmodule Pleroma.Web.ActivityPub.Visibility 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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.CommonAPI 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController 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 @@ defmodule Pleroma.Web.PleromaAPI.AccountController 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 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships 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 @@ defmodule Pleroma.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 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.RelayTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest 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 @@ defmodule Pleroma.Web.StreamerTest 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 @@ defmodule Pleroma.Web.StreamerTest do 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 @@ defmodule Pleroma.Web.StreamerTest do 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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 @@ defmodule Pleroma.FollowingRelationship 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 @@ defmodule Pleroma.UserTest 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 --- priv/repo/migrations/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 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships 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 +++-- test/web/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 @@ defmodule Pleroma.Web.ActivityPub.Relay do @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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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. --- priv/repo/migrations/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 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships 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 --- .../20191008132217_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 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships 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 @@ defmodule Pleroma.LoadTesting.Fetcher 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 @@ defmodule Pleroma.LoadTesting.Fetcher 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 @@ defmodule Pleroma.LoadTesting.Generator 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