[#1335] Refactored UserMute and UserBlock into UserRelationship, introduced EctoEnum.
authorIvan Tashkinov <ivantashkinov@gmail.com>
Mon, 18 Nov 2019 17:38:56 +0000 (20:38 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Mon, 18 Nov 2019 17:38:56 +0000 (20:38 +0300)
15 files changed:
lib/pleroma/ecto_enums.ex [new file with mode: 0644]
lib/pleroma/user.ex
lib/pleroma/user/search.ex
lib/pleroma/user_block.ex [deleted file]
lib/pleroma/user_mute.ex [deleted file]
lib/pleroma/user_relationship.ex [new file with mode: 0644]
lib/pleroma/web/streamer/worker.ex
mix.exs
mix.lock
priv/repo/migrations/20191108161911_create_user_blocks.exs [deleted file]
priv/repo/migrations/20191108173911_data_migration_populate_user_blocks.exs [deleted file]
priv/repo/migrations/20191112151559_create_user_mutes.exs [deleted file]
priv/repo/migrations/20191112151614_data_migration_populate_user_mutes.exs [deleted file]
priv/repo/migrations/20191118084425_create_user_relationships.exs [new file with mode: 0644]
priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs [new file with mode: 0644]

diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex
new file mode 100644 (file)
index 0000000..bad5ec5
--- /dev/null
@@ -0,0 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+import EctoEnum
+
+defenum(UserRelationshipTypeEnum, block: 1, mute: 2, reblog_mute: 3, notification_mute: 4)
index d6bc02d0420437334bd9396a43a956be23535396..4dd03c6c6e7be823f09850438dc8d13122effbc5 100644 (file)
@@ -22,8 +22,7 @@ defmodule Pleroma.User do
   alias Pleroma.Repo
   alias Pleroma.RepoStreamer
   alias Pleroma.User
-  alias Pleroma.UserBlock
-  alias Pleroma.UserMute
+  alias Pleroma.UserRelationship
   alias Pleroma.Web
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
@@ -78,7 +77,6 @@ defmodule Pleroma.User do
     field(:confirmation_token, :string, default: nil)
     field(:default_scope, :string, default: "public")
     field(:domain_blocks, {:array, :string}, default: [])
-    field(:mutes, {:array, :string}, default: [])
     field(:muted_reblogs, {:array, :string}, default: [])
     field(:muted_notifications, {:array, :string}, default: [])
     field(:subscribers, {:array, :string}, default: [])
@@ -121,20 +119,41 @@ defmodule Pleroma.User do
     has_many(:registrations, Registration)
     has_many(:deliveries, Delivery)
 
-    has_many(:blocker_blocks, UserBlock, foreign_key: :blocker_id)
-    has_many(:blockee_blocks, UserBlock, foreign_key: :blockee_id)
-    has_many(:blocked_users, through: [:blocker_blocks, :blockee])
-    has_many(:blocker_users, through: [:blockee_blocks, :blocker])
+    has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
+    has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
+
+    has_many(:blocker_blocks, UserRelationship,
+      foreign_key: :source_id,
+      where: [relationship_type: :block]
+    )
+
+    has_many(:blockee_blocks, UserRelationship,
+      foreign_key: :target_id,
+      where: [relationship_type: :block]
+    )
+
+    has_many(:blocked_users, through: [:blocker_blocks, :target])
+    has_many(:blocker_users, through: [:blockee_blocks, :source])
 
-    has_many(:muter_mutes, UserMute, foreign_key: :muter_id)
-    has_many(:mutee_mutes, UserMute, foreign_key: :mutee_id)
-    has_many(:muted_users, through: [:muter_mutes, :mutee])
-    has_many(:muter_users, through: [:mutee_mutes, :muter])
+    has_many(:muter_mutes, UserRelationship,
+      foreign_key: :source_id,
+      where: [relationship_type: :mute]
+    )
+
+    has_many(:mutee_mutes, UserRelationship,
+      foreign_key: :target_id,
+      where: [relationship_type: :mute]
+    )
+
+    has_many(:muted_users, through: [:muter_mutes, :target])
+    has_many(:muter_users, through: [:mutee_mutes, :source])
 
     field(:info, :map, default: %{})
 
     # `:blocks` is deprecated (replaced with `blocked_users` relation)
     field(:blocks, {:array, :string}, default: [])
+    # `:mutes` is deprecated (replaced with `muted_users` relation)
+    field(:mutes, {:array, :string}, default: [])
 
     timestamps()
   end
@@ -1054,7 +1073,7 @@ defmodule Pleroma.User do
   def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
 
   def mutes_user?(%User{} = user, %User{} = target) do
-    UserMute.exists?(user, target)
+    UserRelationship.mute_exists?(user, target)
   end
 
   @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
@@ -1070,7 +1089,7 @@ defmodule Pleroma.User do
   end
 
   def blocks_user?(%User{} = user, %User{} = target) do
-    UserBlock.exists?(user, target)
+    UserRelationship.block_exists?(user, target)
   end
 
   def blocks_user?(_, _), do: false
@@ -1119,33 +1138,20 @@ defmodule Pleroma.User do
     |> Repo.all()
   end
 
-  defp related_ap_ids_sql(join_table, source_column, target_column) do
-    "(SELECT array_agg(u.ap_id) FROM users as u " <>
-      "INNER JOIN #{join_table} AS join_table " <>
-      "ON join_table.#{source_column} = $1 " <>
-      "WHERE u.id = join_table.#{target_column})"
-  end
-
-  @related_ap_ids_sql_params %{
-    blocked_users: ["user_blocks", "blocker_id", "blockee_id"],
-    muted_users: ["user_mutes", "muter_id", "mutee_id"]
-  }
-
-  def related_ap_ids(user, relations) when is_list(relations) do
-    query =
-      relations
-      |> Enum.map(fn r -> @related_ap_ids_sql_params[r] end)
-      |> Enum.filter(& &1)
-      |> Enum.map(fn [join_table, source_column, target_column] ->
-        related_ap_ids_sql(join_table, source_column, target_column)
-      end)
-      |> Enum.join(", ")
-
-    with {:ok, %{rows: [ap_ids_arrays]}} <-
-           Repo.query("SELECT #{query}", [FlakeId.from_string(user.id)]) do
-      ap_ids_arrays = Enum.map(ap_ids_arrays, &(&1 || []))
-      {:ok, ap_ids_arrays}
-    end
+  @doc """
+  Returns map of related AP IDs list by relation type.
+  E.g. `related_ap_ids(user, [:blocks])` -> `%{blocks: ["https://some.site/users/userapid"]}`
+  """
+  @spec related_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
+  def related_ap_ids(%User{} = user, relationship_types) when is_list(relationship_types) do
+    user
+    |> assoc(:outgoing_relationships)
+    |> join(:inner, [user_rel], u in assoc(user_rel, :target))
+    |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
+    |> select([user_rel, u], [user_rel.relationship_type, fragment("array_agg(?)", u.ap_id)])
+    |> group_by([user_rel, u], user_rel.relationship_type)
+    |> Repo.all()
+    |> Enum.into(%{}, fn [k, v] -> {k, v} end)
   end
 
   @spec subscribers(User.t()) :: [User.t()]
@@ -1918,19 +1924,20 @@ defmodule Pleroma.User do
     set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
   end
 
-  @spec add_to_block(User.t(), User.t()) :: {:ok, UserBlock.t()} | {:error, Ecto.Changeset.t()}
+  @spec add_to_block(User.t(), User.t()) ::
+          {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
   defp add_to_block(%User{} = user, %User{} = blocked) do
-    UserBlock.create(user, blocked)
+    UserRelationship.create_block(user, blocked)
   end
 
   @spec add_to_block(User.t(), User.t()) ::
-          {:ok, UserBlock.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
+          {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
   defp remove_from_block(%User{} = user, %User{} = blocked) do
-    UserBlock.delete(user, blocked)
+    UserRelationship.delete_block(user, blocked)
   end
 
   defp add_to_mutes(%User{} = user, %User{ap_id: ap_id} = muted_user, notifications?) do
-    with {:ok, user_mute} <- UserMute.create(user, muted_user),
+    with {:ok, user_mute} <- UserRelationship.create_mute(user, muted_user),
          {:ok, _user} <-
            set_notification_mutes(
              user,
@@ -1942,7 +1949,7 @@ defmodule Pleroma.User do
   end
 
   defp remove_from_mutes(user, %User{ap_id: ap_id} = muted_user) do
-    with {:ok, user_mute} <- UserMute.delete(user, muted_user),
+    with {:ok, user_mute} <- UserRelationship.delete_mute(user, muted_user),
          {:ok, _user} <-
            set_notification_mutes(
              user,
index 049e1a634d722dc333b22a8376da66fc191d53f0..2b3c7b5b4f08129b36a6dfe7984c3e511b6db94b 100644 (file)
@@ -100,11 +100,11 @@ defmodule Pleroma.User.Search do
 
   defp filter_blocked_user(query, %User{} = blocker) do
     query
-    |> join(:left, [u], b in Pleroma.UserBlock,
+    |> join(:left, [u], b in Pleroma.UserRelationship,
       as: :blocks,
-      on: b.blocker_id == ^blocker.id and u.id == b.blockee_id
+      on: b.relationship_type == ^:block and b.source_id == ^blocker.id and u.id == b.target_id
     )
-    |> where([blocks: b], is_nil(b.blockee_id))
+    |> where([blocks: b], is_nil(b.target_id))
   end
 
   defp filter_blocked_user(query, _), do: query
diff --git a/lib/pleroma/user_block.ex b/lib/pleroma/user_block.ex
deleted file mode 100644 (file)
index bcf4b64..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.UserBlock do
-  use Ecto.Schema
-
-  import Ecto.Changeset
-  import Ecto.Query
-
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.UserBlock
-
-  schema "user_blocks" do
-    belongs_to(:blocker, User, type: FlakeId.Ecto.CompatType)
-    belongs_to(:blockee, User, type: FlakeId.Ecto.CompatType)
-
-    timestamps(updated_at: false)
-  end
-
-  def changeset(%UserBlock{} = user_block, params \\ %{}) do
-    user_block
-    |> cast(params, [:blocker_id, :blockee_id])
-    |> validate_required([:blocker_id, :blockee_id])
-    |> unique_constraint(:blockee_id, name: :user_blocks_blocker_id_blockee_id_index)
-    |> validate_not_self_block()
-  end
-
-  def exists?(%User{} = blocker, %User{} = blockee) do
-    UserBlock
-    |> where(blocker_id: ^blocker.id, blockee_id: ^blockee.id)
-    |> Repo.exists?()
-  end
-
-  def create(%User{} = blocker, %User{} = blockee) do
-    %UserBlock{}
-    |> changeset(%{blocker_id: blocker.id, blockee_id: blockee.id})
-    |> Repo.insert(
-      on_conflict: :replace_all_except_primary_key,
-      conflict_target: [:blocker_id, :blockee_id]
-    )
-  end
-
-  def delete(%User{} = blocker, %User{} = blockee) do
-    attrs = %{blocker_id: blocker.id, blockee_id: blockee.id}
-
-    case Repo.get_by(UserBlock, attrs) do
-      %UserBlock{} = existing_record -> Repo.delete(existing_record)
-      nil -> {:ok, nil}
-    end
-  end
-
-  defp validate_not_self_block(%Ecto.Changeset{} = changeset) do
-    changeset
-    |> validate_change(:blockee_id, fn _, blockee_id ->
-      if blockee_id == get_field(changeset, :blocker_id) do
-        [blockee_id: "can't be equal to blocker_id"]
-      else
-        []
-      end
-    end)
-    |> validate_change(:blocker_id, fn _, blocker_id ->
-      if blocker_id == get_field(changeset, :blockee_id) do
-        [blocker_id: "can't be equal to blockee_id"]
-      else
-        []
-      end
-    end)
-  end
-end
diff --git a/lib/pleroma/user_mute.ex b/lib/pleroma/user_mute.ex
deleted file mode 100644 (file)
index 417a5ff..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.UserMute do
-  use Ecto.Schema
-
-  import Ecto.Changeset
-  import Ecto.Query
-
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.UserMute
-
-  schema "user_mutes" do
-    belongs_to(:muter, User, type: FlakeId.Ecto.CompatType)
-    belongs_to(:mutee, User, type: FlakeId.Ecto.CompatType)
-
-    timestamps(updated_at: false)
-  end
-
-  def changeset(%UserMute{} = user_mute, params \\ %{}) do
-    user_mute
-    |> cast(params, [:muter_id, :mutee_id])
-    |> validate_required([:muter_id, :mutee_id])
-    |> unique_constraint(:mutee_id, name: :user_mutes_muter_id_mutee_id_index)
-    |> validate_not_self_mute()
-  end
-
-  def exists?(%User{} = muter, %User{} = mutee) do
-    UserMute
-    |> where(muter_id: ^muter.id, mutee_id: ^mutee.id)
-    |> Repo.exists?()
-  end
-
-  def create(%User{} = muter, %User{} = mutee) do
-    %UserMute{}
-    |> changeset(%{muter_id: muter.id, mutee_id: mutee.id})
-    |> Repo.insert(
-      on_conflict: :replace_all_except_primary_key,
-      conflict_target: [:muter_id, :mutee_id]
-    )
-  end
-
-  def delete(%User{} = muter, %User{} = mutee) do
-    attrs = %{muter_id: muter.id, mutee_id: mutee.id}
-
-    case Repo.get_by(UserMute, attrs) do
-      %UserMute{} = existing_record -> Repo.delete(existing_record)
-      nil -> {:ok, nil}
-    end
-  end
-
-  defp validate_not_self_mute(%Ecto.Changeset{} = changeset) do
-    changeset
-    |> validate_change(:mutee_id, fn _, mutee_id ->
-      if mutee_id == get_field(changeset, :muter_id) do
-        [mutee_id: "can't be equal to muter_id"]
-      else
-        []
-      end
-    end)
-    |> validate_change(:muter_id, fn _, muter_id ->
-      if muter_id == get_field(changeset, :mutee_id) do
-        [muter_id: "can't be equal to mutee_id"]
-      else
-        []
-      end
-    end)
-  end
-end
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
new file mode 100644 (file)
index 0000000..5cb99ae
--- /dev/null
@@ -0,0 +1,90 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.UserRelationship do
+  use Ecto.Schema
+
+  import Ecto.Changeset
+  import Ecto.Query
+
+  alias Pleroma.Repo
+  alias Pleroma.User
+  alias Pleroma.UserRelationship
+
+  schema "user_relationships" do
+    belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
+    belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
+    field(:relationship_type, UserRelationshipTypeEnum)
+
+    timestamps(updated_at: false)
+  end
+
+  def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
+    user_relationship
+    |> cast(params, [:relationship_type, :source_id, :target_id])
+    |> validate_required([:relationship_type, :source_id, :target_id])
+    |> unique_constraint(:relationship_type,
+      name: :user_relationships_source_id_relationship_type_target_id_index
+    )
+    |> validate_not_self_relationship()
+  end
+
+  def exists?(relationship_type, %User{} = source, %User{} = target) do
+    UserRelationship
+    |> where(relationship_type: ^relationship_type, source_id: ^source.id, target_id: ^target.id)
+    |> Repo.exists?()
+  end
+
+  def block_exists?(%User{} = blocker, %User{} = blockee), do: exists?(:block, blocker, blockee)
+
+  def mute_exists?(%User{} = muter, %User{} = mutee), do: exists?(:mute, muter, mutee)
+
+  def create(relationship_type, %User{} = source, %User{} = target) do
+    %UserRelationship{}
+    |> changeset(%{
+      relationship_type: relationship_type,
+      source_id: source.id,
+      target_id: target.id
+    })
+    |> Repo.insert(
+      on_conflict: :replace_all_except_primary_key,
+      conflict_target: [:source_id, :relationship_type, :target_id]
+    )
+  end
+
+  def create_block(%User{} = blocker, %User{} = blockee), do: create(:block, blocker, blockee)
+
+  def create_mute(%User{} = muter, %User{} = mutee), do: create(:mute, muter, mutee)
+
+  def delete(relationship_type, %User{} = source, %User{} = target) do
+    attrs = %{relationship_type: relationship_type, source_id: source.id, target_id: target.id}
+
+    case Repo.get_by(UserRelationship, attrs) do
+      %UserRelationship{} = existing_record -> Repo.delete(existing_record)
+      nil -> {:ok, nil}
+    end
+  end
+
+  def delete_block(%User{} = blocker, %User{} = blockee), do: delete(:block, blocker, blockee)
+
+  def delete_mute(%User{} = muter, %User{} = mutee), do: delete(:mute, muter, mutee)
+
+  defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
+    changeset
+    |> validate_change(:target_id, fn _, target_id ->
+      if target_id == get_field(changeset, :source_id) do
+        [target_id: "can't be equal to source_id"]
+      else
+        []
+      end
+    end)
+    |> validate_change(:source_id, fn _, source_id ->
+      if source_id == get_field(changeset, :target_id) do
+        [source_id: "can't be equal to target_id"]
+      else
+        []
+      end
+    end)
+  end
+end
index 45f9c7c5328eabb5cfbcd6dbe4069bd95247af48..020112949cc031b4b21155eb015dfa6778d3d712 100644 (file)
@@ -129,7 +129,9 @@ defmodule Pleroma.Web.Streamer.Worker do
   end
 
   defp should_send?(%User{} = user, %Activity{} = item) do
-    {:ok, [blocks, mutes]} = User.related_ap_ids(user, [:blocked_users, :muted_users])
+    related_ap_ids = User.related_ap_ids(user, [:block, :mute])
+    blocks = related_ap_ids[:block] || []
+    mutes = related_ap_ids[:mute] || []
     reblog_mutes = user.muted_reblogs || []
     recipient_blocks = MapSet.new(blocks ++ mutes)
     recipients = MapSet.new(item.recipients)
diff --git a/mix.exs b/mix.exs
index 81ce4f25cef74d619776b7a7b6b18a569ff014f8..c870b330f31fbefe4101c3a0e98cbf62713836b9 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -100,6 +100,7 @@ defmodule Pleroma.Mixfile do
       {:plug_cowboy, "~> 2.0"},
       {:phoenix_pubsub, "~> 1.1"},
       {:phoenix_ecto, "~> 4.0"},
+      {:ecto_enum, "~> 1.4"},
       {:ecto_sql, "~> 3.2"},
       {:postgrex, ">= 0.13.5"},
       {:oban, "~> 0.8.1"},
index d4a80df77ccce408db16e0806333bfb24258bb6f..fc092b79feecd10690694bb572c31d62652b85e9 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -24,6 +24,7 @@
   "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
   "earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [:mix], [], "hexpm"},
   "ecto": {:hex, :ecto, "3.2.3", "51274df79862845b388733fddcf6f107d0c8c86e27abe7131fa98f8d30761bda", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+  "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
   "ecto_sql": {:hex, :ecto_sql, "3.2.0", "751cea597e8deb616084894dd75cbabfdbe7255ff01e8c058ca13f0353a3921b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
   "esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
   "eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
diff --git a/priv/repo/migrations/20191108161911_create_user_blocks.exs b/priv/repo/migrations/20191108161911_create_user_blocks.exs
deleted file mode 100644 (file)
index c882d2b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-defmodule Pleroma.Repo.Migrations.CreateUserBlocks do
-  use Ecto.Migration
-
-  def change do
-    create_if_not_exists table(:user_blocks) do
-      add(:blocker_id, references(:users, type: :uuid, on_delete: :delete_all))
-      add(:blockee_id, references(:users, type: :uuid, on_delete: :delete_all))
-
-      timestamps(updated_at: false)
-    end
-
-    create_if_not_exists(unique_index(:user_blocks, [:blocker_id, :blockee_id]))
-  end
-end
diff --git a/priv/repo/migrations/20191108173911_data_migration_populate_user_blocks.exs b/priv/repo/migrations/20191108173911_data_migration_populate_user_blocks.exs
deleted file mode 100644 (file)
index fe53767..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserBlocks do
-  use Ecto.Migration
-
-  alias Ecto.Adapters.SQL
-  alias Pleroma.Repo
-
-  require Logger
-
-  def up do
-    {:ok, %{rows: block_rows}} =
-      SQL.query(Repo, "SELECT id, blocks FROM users WHERE blocks != '{}'")
-
-    blockee_ap_ids =
-      Enum.flat_map(
-        block_rows,
-        fn [_, ap_ids] -> ap_ids end
-      )
-      |> Enum.uniq()
-
-    # Selecting ids of all blockees at once in order to reduce the number of SELECT queries
-    {:ok, %{rows: blockee_ap_id_id}} =
-      SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [blockee_ap_ids])
-
-    blockee_id_by_ap_id = Enum.into(blockee_ap_id_id, %{}, fn [k, v] -> {k, v} end)
-
-    Enum.each(
-      block_rows,
-      fn [blocker_id, blockee_ap_ids] ->
-        blocker_uuid = Ecto.UUID.cast!(blocker_id)
-
-        for blockee_ap_id <- blockee_ap_ids do
-          blockee_id = blockee_id_by_ap_id[blockee_ap_id]
-
-          with {:ok, blockee_uuid} <- blockee_id && Ecto.UUID.cast(blockee_id) do
-            execute(
-              "INSERT INTO user_blocks(blocker_id, blockee_id, inserted_at) " <>
-                "VALUES('#{blocker_uuid}'::uuid, '#{blockee_uuid}'::uuid, now()) " <>
-                "ON CONFLICT (blocker_id, blockee_id) DO NOTHING"
-            )
-          else
-            _ -> Logger.warn("Missing reference: (#{blocker_uuid}, #{blockee_id})")
-          end
-        end
-      end
-    )
-  end
-
-  def down, do: :noop
-end
diff --git a/priv/repo/migrations/20191112151559_create_user_mutes.exs b/priv/repo/migrations/20191112151559_create_user_mutes.exs
deleted file mode 100644 (file)
index eaa285d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-defmodule Pleroma.Repo.Migrations.CreateUserMutes do
-  use Ecto.Migration
-
-  def change do
-    create_if_not_exists table(:user_mutes) do
-      add(:muter_id, references(:users, type: :uuid, on_delete: :delete_all))
-      add(:mutee_id, references(:users, type: :uuid, on_delete: :delete_all))
-
-      timestamps(updated_at: false)
-    end
-
-    create_if_not_exists(unique_index(:user_mutes, [:muter_id, :mutee_id]))
-  end
-end
diff --git a/priv/repo/migrations/20191112151614_data_migration_populate_user_mutes.exs b/priv/repo/migrations/20191112151614_data_migration_populate_user_mutes.exs
deleted file mode 100644 (file)
index a8bdd07..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserMutes do
-  use Ecto.Migration
-
-  alias Ecto.Adapters.SQL
-  alias Pleroma.Repo
-
-  require Logger
-
-  def up do
-    {:ok, %{rows: mute_rows}} = SQL.query(Repo, "SELECT id, mutes FROM users WHERE mutes != '{}'")
-
-    mutee_ap_ids =
-      Enum.flat_map(
-        mute_rows,
-        fn [_, ap_ids] -> ap_ids end
-      )
-      |> Enum.uniq()
-
-    # Selecting ids of all mutees at once in order to reduce the number of SELECT queries
-    {:ok, %{rows: mutee_ap_id_id}} =
-      SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [mutee_ap_ids])
-
-    mutee_id_by_ap_id = Enum.into(mutee_ap_id_id, %{}, fn [k, v] -> {k, v} end)
-
-    Enum.each(
-      mute_rows,
-      fn [muter_id, mutee_ap_ids] ->
-        muter_uuid = Ecto.UUID.cast!(muter_id)
-
-        for mutee_ap_id <- mutee_ap_ids do
-          mutee_id = mutee_id_by_ap_id[mutee_ap_id]
-
-          with {:ok, mutee_uuid} <- mutee_id && Ecto.UUID.cast(mutee_id) do
-            execute(
-              "INSERT INTO user_mutes(muter_id, mutee_id, inserted_at) " <>
-                "VALUES('#{muter_uuid}'::uuid, '#{mutee_uuid}'::uuid, now()) " <>
-                "ON CONFLICT (muter_id, mutee_id) DO NOTHING"
-            )
-          else
-            _ -> Logger.warn("Missing reference: (#{muter_uuid}, #{mutee_id})")
-          end
-        end
-      end
-    )
-  end
-
-  def down, do: :noop
-end
diff --git a/priv/repo/migrations/20191118084425_create_user_relationships.exs b/priv/repo/migrations/20191118084425_create_user_relationships.exs
new file mode 100644 (file)
index 0000000..c281f88
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.CreateUserRelationships do
+  use Ecto.Migration
+
+  def change do
+    create_if_not_exists table(:user_relationships) do
+      add(:source_id, references(:users, type: :uuid, on_delete: :delete_all))
+      add(:target_id, references(:users, type: :uuid, on_delete: :delete_all))
+      add(:relationship_type, :integer, null: false)
+
+      timestamps(updated_at: false)
+    end
+
+    create_if_not_exists(
+      unique_index(:user_relationships, [:source_id, :relationship_type, :target_id])
+    )
+  end
+end
diff --git a/priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs b/priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs
new file mode 100644 (file)
index 0000000..f8dde76
--- /dev/null
@@ -0,0 +1,64 @@
+defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserRelationships do
+  use Ecto.Migration
+
+  alias Ecto.Adapters.SQL
+  alias Pleroma.Repo
+
+  require Logger
+
+  def up do
+    Enum.each(
+      [blocks: 1, mutes: 2, muted_reblogs: 3, muted_notifications: 4],
+      fn {field, relationship_type_code} ->
+        migrate(field, relationship_type_code)
+      end
+    )
+  end
+
+  def down, do: :noop
+
+  defp migrate(field, relationship_type_code) do
+    Logger.info("Processing users.#{field}...")
+
+    {:ok, %{rows: field_rows}} =
+      SQL.query(Repo, "SELECT id, #{field} FROM users WHERE #{field} != '{}'")
+
+    target_ap_ids =
+      Enum.flat_map(
+        field_rows,
+        fn [_, ap_ids] -> ap_ids end
+      )
+      |> Enum.uniq()
+
+    # Selecting ids of all targets at once in order to reduce the number of SELECT queries
+    {:ok, %{rows: target_ap_id_id}} =
+      SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [target_ap_ids])
+
+    target_id_by_ap_id = Enum.into(target_ap_id_id, %{}, fn [k, v] -> {k, v} end)
+
+    Enum.each(
+      field_rows,
+      fn [source_id, target_ap_ids] ->
+        source_uuid = Ecto.UUID.cast!(source_id)
+
+        for target_ap_id <- target_ap_ids do
+          target_id = target_id_by_ap_id[target_ap_id]
+
+          with {:ok, target_uuid} <- target_id && Ecto.UUID.cast(target_id) do
+            execute("""
+            INSERT INTO user_relationships(
+              source_id, target_id, relationship_type, inserted_at
+            )
+            VALUES(
+              '#{source_uuid}'::uuid, '#{target_uuid}'::uuid, #{relationship_type_code}, now()
+            )
+            ON CONFLICT (source_id, relationship_type, target_id) DO NOTHING
+            """)
+          else
+            _ -> Logger.warn("Unresolved #{field} reference: (#{source_uuid}, #{target_id})")
+          end
+        end
+      end
+    )
+  end
+end