From e4a446003d890bd8d2089d2941b8f9f6ad5ded99 Mon Sep 17 00:00:00 2001 From: miffigriffy Date: Wed, 11 Sep 2019 03:16:37 +0200 Subject: [PATCH] Refactoring of Actors context --- lib/mix/tasks/mobilizon/create_bot.ex | 2 +- lib/mobilizon/actors/actors.ex | 1325 ++++++++--------- lib/mobilizon/actors/follower.ex | 7 +- lib/mobilizon/addresses/addresses.ex | 7 +- lib/mobilizon/media/media.ex | 7 +- lib/mobilizon/users/user.ex | 7 +- lib/mobilizon/users/users.ex | 14 +- lib/mobilizon_web/api/follows.ex | 2 +- lib/mobilizon_web/resolvers/member.ex | 2 +- lib/mobilizon_web/resolvers/person.ex | 2 +- .../views/activity_pub/actor_view.ex | 8 +- lib/service/activity_pub/activity_pub.ex | 2 +- lib/service/activity_pub/relay.ex | 2 +- lib/service/activity_pub/transmogrifier.ex | 2 +- test/mobilizon/actors/actors_test.exs | 17 +- .../activity_pub/transmogrifier_test.exs | 22 +- test/tasks/relay_test.exs | 6 +- 17 files changed, 671 insertions(+), 763 deletions(-) diff --git a/lib/mix/tasks/mobilizon/create_bot.ex b/lib/mix/tasks/mobilizon/create_bot.ex index c4b85dd2..13c32131 100644 --- a/lib/mix/tasks/mobilizon/create_bot.ex +++ b/lib/mix/tasks/mobilizon/create_bot.ex @@ -15,7 +15,7 @@ defmodule Mix.Tasks.Mobilizon.CreateBot do Mix.Task.run("app.start") with {:ok, %User{} = user} <- Users.get_user_by_email(email, true), - actor <- Actors.register_bot_account(%{name: name, summary: summary}), + actor <- Actors.register_bot(%{name: name, summary: summary}), {:ok, %Bot{} = bot} <- Actors.create_bot(%{ "type" => type, diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index eb4da0fb..ca955382 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -9,7 +9,7 @@ defmodule Mobilizon.Actors do alias Ecto.Multi alias Mobilizon.Actors.{Actor, Bot, Follower, Member} - alias Mobilizon.Crypto + alias Mobilizon.{Crypto, Events} alias Mobilizon.Media.File alias Mobilizon.Storage.{Page, Repo} @@ -45,6 +45,8 @@ defmodule Mobilizon.Actors do :creator ]) + @administrator_roles [:creator, :administrator] + @doc false @spec data :: Dataloader.Ecto.t() def data, do: Dataloader.Ecto.new(Repo, query: &query/2) @@ -61,7 +63,7 @@ defmodule Mobilizon.Actors do @doc """ Gets a single actor. - Raises `Ecto.NoResultsError` if the Actor does not exist. + Raises `Ecto.NoResultsError` if the actor does not exist. """ @spec get_actor!(integer | String.t()) :: Actor.t() def get_actor!(id), do: Repo.get!(Actor, id) @@ -166,40 +168,15 @@ defmodule Mobilizon.Actors do def get_cached_local_actor_by_name(name) do Cachex.fetch(:activity_pub, "actor_" <> name, fn "actor_" <> name -> case get_local_actor_by_name(name) do - nil -> {:ignore, nil} - %Actor{} = actor -> {:commit, actor} + nil -> + {:ignore, nil} + + %Actor{} = actor -> + {:commit, actor} end end) end - @doc """ - Gets local actors by their username. - """ - @spec get_local_actor_by_username(String.t()) :: [Actor.t()] - def get_local_actor_by_username(username) do - username - |> actor_by_username_query() - |> filter_local() - |> Repo.all() - |> Repo.preload(:organized_events) - end - - @doc """ - Builds a page struct for actors by their name or displayed name. - """ - @spec build_actors_by_username_or_name_page( - String.t(), - [ActorType.t()], - integer | nil, - integer | nil - ) :: Page.t() - def build_actors_by_username_or_name_page(username, types, page \\ nil, limit \\ nil) do - username - |> actor_by_username_or_name_query() - |> filter_by_types(types) - |> Page.build_page(page, limit) - end - @doc """ Creates an actor. """ @@ -210,6 +187,23 @@ defmodule Mobilizon.Actors do |> Repo.insert() end + @doc """ + Creates a new person actor. + """ + @spec new_person(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()} + def new_person(args) do + args = Map.put(args, :keys, Crypto.generate_rsa_2048_private_key()) + + with {:ok, %Actor{} = person} <- + %Actor{} + |> Actor.registration_changeset(args) + |> Repo.insert() do + Events.create_feed_token(%{"user_id" => args["user_id"], "actor_id" => person.id}) + + {:ok, person} + end + end + @doc """ Updates an actor. """ @@ -277,6 +271,34 @@ defmodule Mobilizon.Actors do @spec list_actors :: [Actor.t()] def list_actors, do: Repo.all(Actor) + @doc """ + Returns the list of local actors by their username. + """ + @spec list_local_actor_by_username(String.t()) :: [Actor.t()] + def list_local_actor_by_username(username) do + username + |> actor_by_username_query() + |> filter_local() + |> Repo.all() + |> Repo.preload(:organized_events) + end + + @doc """ + Builds a page struct for actors by their name or displayed name. + """ + @spec build_actors_by_username_or_name_page( + String.t(), + [ActorType.t()], + integer | nil, + integer | nil + ) :: Page.t() + def build_actors_by_username_or_name_page(username, types, page \\ nil, limit \\ nil) do + username + |> actor_by_username_or_name_query() + |> filter_by_types(types) + |> Page.build_page(page, limit) + end + @doc """ Gets a group by its title. """ @@ -297,11 +319,453 @@ defmodule Mobilizon.Actors do |> Repo.one() end + @doc """ + Gets a group by its actor id. + """ + @spec get_group_by_actor_id(integer | String.t()) :: + {:ok, Actor.t()} | {:error, :group_not_found} + def get_group_by_actor_id(actor_id) do + case Repo.get_by(Actor, id: actor_id, type: :Group) do + nil -> + {:error, :group_not_found} + + actor -> + {:ok, actor} + end + end + + @doc """ + Creates a group. + """ + @spec create_group(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()} + def create_group(attrs \\ %{}) do + %Actor{} + |> Actor.group_creation(attrs) + |> Repo.insert() + end + + @doc """ + Deletes a group. + """ + def delete_group!(%Actor{type: :Group} = group), do: Repo.delete!(group) + + @doc """ + Lists the groups. + """ + @spec list_groups(integer | nil, integer | nil) :: [Actor.t()] + def list_groups(page \\ nil, limit \\ nil) do + groups_query() + |> Page.paginate(page, limit) + |> Repo.all() + end + + @doc """ + Returns the list of groups an actor is member of. + """ + @spec list_groups_member_of(Actor.t()) :: [Actor.t()] + def list_groups_member_of(%Actor{id: actor_id}) do + actor_id + |> groups_member_of_query() + |> Repo.all() + end + + @doc """ + Gets a single member. + Raises `Ecto.NoResultsError` if the member does not exist. + """ + @spec get_member!(integer | String.t()) :: Member.t() + def get_member!(id), do: Repo.get!(Member, id) + + @doc """ + Gets a single member of an actor (for example a group). + """ + @spec get_member(integer | String.t(), integer | String.t()) :: + {:ok, Member.t()} | {:error, :member_not_found} + def get_member(actor_id, parent_id) do + case Repo.get_by(Member, actor_id: actor_id, parent_id: parent_id) do + nil -> + {:error, :member_not_found} + + member -> + {:ok, member} + end + end + + @doc """ + Creates a member. + """ + @spec create_member(map) :: {:ok, Member.t()} | {:error, Ecto.Changeset.t()} + def create_member(attrs \\ %{}) do + with {:ok, %Member{} = member} <- + %Member{} + |> Member.changeset(attrs) + |> Repo.insert() do + {:ok, Repo.preload(member, [:actor, :parent])} + end + end + + @doc """ + Updates a member. + """ + @spec update_member(Member.t(), map) :: {:ok, Member.t()} | {:error, Ecto.Changeset.t()} + def update_member(%Member{} = member, attrs) do + member + |> Member.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a member. + """ + @spec delete_member(Member.t()) :: {:ok, Member.t()} | {:error, Ecto.Changeset.t()} + def delete_member(%Member{} = member), do: Repo.delete(member) + + @doc """ + Returns the list of members for an actor. + """ + @spec list_members_for_actor(Actor.t()) :: [Member.t()] + def list_members_for_actor(%Actor{id: actor_id}) do + actor_id + |> members_for_actor_query() + |> Repo.all() + end + + @doc """ + Returns the list of members for a group. + """ + @spec list_members_for_group(Actor.t()) :: [Member.t()] + def list_members_for_group(%Actor{id: group_id, type: :Group}) do + group_id + |> members_for_group_query() + |> Repo.all() + end + + @doc """ + Returns the list of administrator members for a group. + """ + @spec list_administrator_members_for_group(integer | String.t(), integer | nil, integer | nil) :: + [Member.t()] + def list_administrator_members_for_group(id, page \\ nil, limit \\ nil) do + id + |> administrator_members_for_group_query() + |> Page.paginate(page, limit) + |> Repo.all() + end + + @doc """ + Returns the list of all group ids where the actor_id is the last administrator. + """ + @spec list_group_ids_where_last_administrator(integer | String.t()) :: [integer] + def list_group_ids_where_last_administrator(actor_id) do + actor_id + |> group_ids_where_last_administrator_query() + |> Repo.all() + end + + @doc """ + Gets a single bot. + Raises `Ecto.NoResultsError` if the bot does not exist. + """ + def get_bot!(id), do: Repo.get!(Bot, id) + + @doc """ + Gets the bot associated to an actor. + """ + @spec get_bot_for_actor(Actor.t()) :: Bot.t() + def get_bot_for_actor(%Actor{id: actor_id}) do + Repo.get_by!(Bot, actor_id: actor_id) + end + + @doc """ + Creates a bot. + """ + @spec create_bot(map) :: {:ok, Bot.t()} | {:error, Ecto.Changeset.t()} + def create_bot(attrs \\ %{}) do + %Bot{} + |> Bot.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Registers a new bot. + """ + @spec register_bot(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()} + def register_bot(%{name: name, summary: summary}) do + attrs = %{ + preferred_username: name, + domain: nil, + keys: Crypto.generate_rsa_2048_private_key(), + summary: summary, + type: :Service + } + + %Actor{} + |> Actor.registration_changeset(attrs) + |> Repo.insert() + end + + @spec get_or_create_actor_by_url(String.t(), String.t()) :: + {:ok, Actor.t()} | {:error, Ecto.Changeset.t()} + def get_or_create_actor_by_url(url, preferred_username \\ "relay") do + case get_actor_by_url(url) do + {:ok, %Actor{} = actor} -> + {:ok, actor} + + _ -> + %{url: url, preferred_username: preferred_username} + |> Actor.relay_creation_changeset() + |> Repo.insert() + end + end + + @doc """ + Updates a bot. + """ + @spec update_bot(Bot.t(), map) :: {:ok, Bot.t()} | {:error, Ecto.Changeset.t()} + def update_bot(%Bot{} = bot, attrs) do + bot + |> Bot.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a bot. + """ + @spec delete_bot(Bot.t()) :: {:ok, Bot.t()} | {:error, Ecto.Changeset.t()} + def delete_bot(%Bot{} = bot), do: Repo.delete(bot) + + @doc """ + Returns the list of bots. + """ + @spec list_bots :: [Bot.t()] + def list_bots, do: Repo.all(Bot) + + @doc """ + Gets a single follower. + Raises `Ecto.NoResultsError` if the follower does not exist. + """ + @spec get_follower!(integer | String.t()) :: Follower.t() + def get_follower!(id) do + Follower + |> Repo.get!(id) + |> Repo.preload([:actor, :target_actor]) + end + + @doc """ + Get a follower by the url. + """ + @spec get_follower_by_url(String.t()) :: Follower.t() + def get_follower_by_url(url) do + url + |> follower_by_url() + |> Repo.one() + end + + @doc """ + Gets a follower by the followed actor and following actor + """ + @spec get_follower_by_followed_and_following(Actor.t(), Actor.t()) :: Follower.t() | nil + def get_follower_by_followed_and_following(%Actor{id: followed_id}, %Actor{id: following_id}) do + followed_id + |> follower_by_followed_and_following_query(following_id) + |> Repo.one() + end + + @doc """ + Creates a follower. + """ + @spec create_follower(map) :: {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} + def create_follower(attrs \\ %{}) do + with {:ok, %Follower{} = follower} <- + %Follower{} + |> Follower.changeset(attrs) + |> Repo.insert() do + {:ok, Repo.preload(follower, [:actor, :target_actor])} + end + end + + @doc """ + Updates a follower. + """ + @spec update_follower(Follower.t(), map) :: {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} + def update_follower(%Follower{} = follower, attrs) do + follower + |> Follower.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a follower. + """ + @spec delete_follower(Follower.t()) :: {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} + def delete_follower(%Follower{} = follower), do: Repo.delete(follower) + + @doc """ + Deletes a follower by followed and following actors. + """ + @spec delete_follower_by_followed_and_following(Actor.t(), Actor.t()) :: + {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} + def delete_follower_by_followed_and_following(%Actor{} = followed, %Actor{} = following) do + followed + |> get_follower_by_followed_and_following(following) + |> Repo.delete() + end + + @doc """ + Returns the list of followers for an actor. + If actor A and C both follow actor B, actor B's followers are A and C. + """ + @spec list_followers_for_actor(Actor.t()) :: [Follower.t()] + def list_followers_for_actor(%Actor{id: actor_id}) do + actor_id + |> followers_for_actor_query() + |> Repo.all() + end + + @doc """ + Returns the list of external followers for an actor. + """ + @spec list_external_followers_for_actor(Actor.t()) :: [Follower.t()] + def list_external_followers_for_actor(%Actor{id: actor_id}) do + actor_id + |> followers_for_actor_query() + |> filter_external() + |> Repo.all() + end + + @doc """ + Build a page struct for followers of an actor. + """ + @spec build_followers_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t() + def build_followers_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do + actor_id + |> followers_for_actor_query() + |> Page.build_page(page, limit) + end + + @doc """ + Returns the list of followings for an actor. + If actor A follows actor B and C, actor A's followings are B and C. + """ + @spec list_followings_for_actor(Actor.t()) :: [Follower.t()] + def list_followings_for_actor(%Actor{id: actor_id}) do + actor_id + |> followings_for_actor_query() + |> Repo.all() + end + + @doc """ + Build a page struct for followings of an actor. + """ + @spec build_followings_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t() + def build_followings_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do + actor_id + |> followings_for_actor_query() + |> Page.build_page(page, limit) + end + + @doc """ + Makes an actor following another actor. + """ + @spec follow(Actor.t(), Actor.t(), String.t() | nil, boolean | nil) :: + {:ok, Follower.t()} | {:error, atom, String.t()} + def follow(%Actor{} = followed, %Actor{} = follower, url \\ nil, approved \\ true) do + with {:suspended, false} <- {:suspended, followed.suspended}, + # Check if followed has blocked follower + {:already_following, nil} <- {:already_following, is_following(follower, followed)} do + Logger.info( + "Making #{follower.preferred_username} follow #{followed.preferred_username} " <> + "(approved: #{approved})" + ) + + create_follower(%{ + "actor_id" => follower.id, + "target_actor_id" => followed.id, + "approved" => approved, + "url" => url + }) + else + {:already_following, %Follower{}} -> + {:error, :already_following, + "Could not follow actor: you are already following #{followed.preferred_username}"} + + {:suspended, _} -> + {:error, :suspended, + "Could not follow actor: #{followed.preferred_username} has been suspended"} + end + end + + @doc """ + Unfollows an actor (removes a Follower record). + """ + @spec unfollow(Actor.t(), Actor.t()) :: + {:ok, Follower.t()} | {:error, Ecto.Changeset.t() | String.t()} + def unfollow(%Actor{} = followed, %Actor{} = follower) do + case {:already_following, is_following(follower, followed)} do + {:already_following, %Follower{} = follow} -> + delete_follower(follow) + + {:already_following, nil} -> + {:error, "Could not unfollow actor: you are not following #{followed.preferred_username}"} + end + end + + @doc """ + Checks whether an actor is following another actor. + """ + @spec is_following(Actor.t(), Actor.t()) :: Follower.t() | nil + def is_following(%Actor{} = follower_actor, %Actor{} = followed_actor) do + get_follower_by_followed_and_following(followed_actor, follower_actor) + end + + @spec remove_banner(Actor.t()) :: {:ok, Actor.t()} + defp remove_banner(%Actor{banner: nil} = actor), do: {:ok, actor} + + defp remove_banner(%Actor{banner: %File{url: url}} = actor) do + safe_remove_file(url, actor) + end + + @spec remove_avatar(Actor.t()) :: {:ok, Actor.t()} + defp remove_avatar(%Actor{avatar: nil} = actor), do: {:ok, actor} + + defp remove_avatar(%Actor{avatar: %File{url: url}} = actor) do + safe_remove_file(url, actor) + end + + @spec safe_remove_file(String.t(), Actor.t()) :: {:ok, Actor.t()} + defp safe_remove_file(url, %Actor{} = actor) do + case MobilizonWeb.Upload.remove(url) do + {:ok, _value} -> + {:ok, actor} + + {:error, error} -> + Logger.error("Error while removing an upload file") + Logger.debug(inspect(error)) + + {:ok, actor} + end + end + + @spec delete_files_if_media_changed(Ecto.Changeset.t()) :: Ecto.Changeset.t() + defp delete_files_if_media_changed(%Ecto.Changeset{changes: changes, data: data} = changeset) do + Enum.each([:avatar, :banner], fn key -> + if Map.has_key?(changes, key) do + with %Ecto.Changeset{changes: %{url: new_url}} <- changes[key], + %{url: old_url} <- data |> Map.from_struct() |> Map.get(key), + false <- new_url == old_url do + MobilizonWeb.Upload.remove(old_url) + end + end + end) + + changeset + end + @spec actor_with_preload_query(integer | String.t()) :: Ecto.Query.t() - defp actor_with_preload_query(id) do + defp actor_with_preload_query(actor_id) do from( a in Actor, - where: a.id == ^id, + where: a.id == ^actor_id, preload: [:organized_events, :followers, :followings] ) end @@ -354,7 +818,113 @@ defmodule Mobilizon.Actors do @spec group_query :: Ecto.Query.t() defp group_query do - from(a in Actor, where: a.type == "Group") + from(a in Actor, where: a.type == ^:Group) + end + + @spec groups_member_of_query(integer | String.t()) :: Ecto.Query.t() + defp groups_member_of_query(actor_id) do + from( + a in Actor, + join: m in Member, + on: a.id == m.parent_id, + where: m.actor_id == ^actor_id + ) + end + + @spec groups_query :: Ecto.Query.t() + defp groups_query do + from( + a in Actor, + where: a.type == ^:Group, + where: a.visibility in ^[:public, :unlisted] + ) + end + + @spec members_for_actor_query(integer | String.t()) :: Ecto.Query.t() + defp members_for_actor_query(actor_id) do + from( + m in Member, + where: m.actor_id == ^actor_id, + preload: [:parent] + ) + end + + @spec members_for_group_query(integer | String.t()) :: Ecto.Query.t() + defp members_for_group_query(group_id) do + from( + m in Member, + where: m.parent_id == ^group_id, + preload: [:parent, :actor] + ) + end + + @spec administrator_members_for_group_query(integer | String.t()) :: Ecto.Query.t() + defp administrator_members_for_group_query(group_id) do + from( + m in Member, + where: m.parent_id == ^group_id and m.role in ^@administrator_roles, + preload: [:actor] + ) + end + + @spec administrator_members_for_actor_query(integer | String.t()) :: Ecto.Query.t() + defp administrator_members_for_actor_query(actor_id) do + from( + m in Member, + where: m.actor_id == ^actor_id and m.role in ^@administrator_roles, + select: m.parent_id + ) + end + + @spec group_ids_where_last_administrator_query(integer | String.t()) :: Ecto.Query.t() + defp group_ids_where_last_administrator_query(actor_id) do + from( + m in Member, + where: m.role in ^@administrator_roles, + join: m2 in subquery(administrator_members_for_actor_query(actor_id)), + on: m.parent_id == m2.parent_id, + group_by: m.parent_id, + select: m.parent_id, + having: count(m.actor_id) == 1 + ) + end + + @spec follower_by_url(String.t()) :: Ecto.Query.t() + defp follower_by_url(url) do + from( + f in Follower, + where: f.url == ^url, + preload: [:actor, :target_actor] + ) + end + + @spec follower_by_followed_and_following_query(integer | String.t(), integer | String.t()) :: + Ecto.Query.t() + defp follower_by_followed_and_following_query(followed_id, follower_id) do + from( + f in Follower, + where: f.target_actor_id == ^followed_id and f.actor_id == ^follower_id + ) + end + + @spec followers_for_actor_query(integer | String.t()) :: Ecto.Query.t() + defp followers_for_actor_query(actor_id) do + from( + a in Actor, + join: f in Follower, + on: a.id == f.actor_id, + where: f.target_actor_id == ^actor_id + ) + end + + @spec followings_for_actor_query(integer | String.t()) :: Ecto.Query.t() + defp followings_for_actor_query(actor_id) do + from( + a in Actor, + join: f in Follower, + on: a.id == f.target_actor_id, + where: f.actor_id == ^actor_id + ) end @spec filter_local(Ecto.Query.t()) :: Ecto.Query.t() @@ -362,6 +932,11 @@ defmodule Mobilizon.Actors do from(a in query, where: is_nil(a.domain)) end + @spec filter_external(Ecto.Query.t()) :: Ecto.Query.t() + defp filter_external(query) do + from(a in query, where: not is_nil(a.domain)) + end + @spec filter_by_type(Ecto.Query.t(), ActorType.t()) :: Ecto.Query.t() defp filter_by_type(query, type) when type in [:Person, :Group] do from(a in query, where: a.type == ^type) @@ -386,684 +961,4 @@ defmodule Mobilizon.Actors do @spec preload_followers(Actor.t(), boolean) :: Actor.t() defp preload_followers(actor, true), do: Repo.preload(actor, [:followers]) defp preload_followers(actor, false), do: actor - - ##### TODO: continue refactoring from here ##### - - @doc """ - Returns the groups an actor is member of - """ - @spec get_groups_member_of(struct()) :: list() - def get_groups_member_of(%Actor{id: actor_id}) do - Repo.all( - from( - a in Actor, - join: m in Member, - on: a.id == m.parent_id, - where: m.actor_id == ^actor_id - ) - ) - end - - @doc """ - Returns the members for a group actor - """ - @spec get_members_for_group(struct()) :: list() - def get_members_for_group(%Actor{id: actor_id}) do - Repo.all( - from( - a in Actor, - join: m in Member, - on: a.id == m.actor_id, - where: m.parent_id == ^actor_id - ) - ) - end - - @doc """ - Creates a group. - - ## Examples - - iex> create_group(%{name: "group name"}) - {:ok, %Mobilizon.Actors.Actor{}} - - iex> create_group(%{name: nil}) - {:error, %Ecto.Changeset{}} - - """ - def create_group(attrs \\ %{}) do - %Actor{} - |> Actor.group_creation(attrs) - |> Repo.insert() - end - - @doc """ - Delete a group - """ - def delete_group!(%Actor{type: :Group} = group) do - Repo.delete!(group) - end - - @doc """ - List the groups - """ - @spec list_groups(number(), number()) :: list(Actor.t()) - def list_groups(page \\ nil, limit \\ nil) do - Repo.all( - from( - a in Actor, - where: a.type == ^:Group, - where: a.visibility in [^:public, ^:unlisted] - ) - |> Page.paginate(page, limit) - ) - end - - @doc """ - Get a group by its actor id - """ - def get_group_by_actor_id(actor_id) do - case Repo.get_by(Actor, id: actor_id, type: :Group) do - nil -> {:error, :group_not_found} - actor -> {:ok, actor} - end - end - - @doc """ - Create a new person actor - """ - @spec new_person(map()) :: {:ok, Actor.t()} | any - def new_person(args) do - args = Map.put(args, :keys, Crypto.generate_rsa_2048_private_key()) - - with {:ok, %Actor{} = person} <- - %Actor{} - |> Actor.registration_changeset(args) - |> Repo.insert() do - Mobilizon.Events.create_feed_token(%{"user_id" => args["user_id"], "actor_id" => person.id}) - {:ok, person} - end - end - - @doc """ - Register a new bot actor. - """ - @spec register_bot_account(map()) :: Actor.t() - def register_bot_account(%{name: name, summary: summary}) do - actor = - Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{ - preferred_username: name, - domain: nil, - keys: Crypto.generate_rsa_2048_private_key(), - summary: summary, - type: :Service - }) - - try do - Repo.insert!(actor) - rescue - e in Ecto.InvalidChangesetError -> - {:error, e.changeset} - end - end - - def get_or_create_service_actor_by_url(url, preferred_username \\ "relay") do - case get_actor_by_url(url) do - {:ok, %Actor{} = actor} -> - {:ok, actor} - - _ -> - %{url: url, preferred_username: preferred_username} - |> Actor.relay_creation_changeset() - |> Repo.insert() - end - end - - @doc """ - Gets a single member of an actor (for example a group) - """ - def get_member(actor_id, parent_id) do - case Repo.get_by(Member, actor_id: actor_id, parent_id: parent_id) do - nil -> {:error, :member_not_found} - member -> {:ok, member} - end - end - - @doc """ - Gets a single member. - - Raises `Ecto.NoResultsError` if the Member does not exist. - - ## Examples - - iex> get_member!(123) - %Mobilizon.Actors.Member{} - - iex> get_member!(456) - ** (Ecto.NoResultsError) - - """ - def get_member!(id), do: Repo.get!(Member, id) - - @doc """ - Creates a member. - - ## Examples - - iex> create_member(%{actor: %Actor{}}) - {:ok, %Mobilizon.Actors.Member{}} - - iex> create_member(%{actor: nil}) - {:error, %Ecto.Changeset{}} - - """ - def create_member(attrs \\ %{}) do - with {:ok, %Member{} = member} <- - %Member{} - |> Member.changeset(attrs) - |> Repo.insert() do - {:ok, Repo.preload(member, [:actor, :parent])} - end - end - - @doc """ - Updates a member. - - ## Examples - - iex> update_member(%Member{}, %{role: 3}) - {:ok, %Mobilizon.Actors.Member{}} - - iex> update_member(%Member{}, %{role: nil}) - {:error, %Ecto.Changeset{}} - - """ - def update_member(%Member{} = member, attrs) do - member - |> Member.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Member. - - ## Examples - - iex> delete_member(%Member{}) - {:ok, %Mobilizon.Actors.Member{}} - - iex> delete_member(%Member{}) - {:error, %Ecto.Changeset{}} - - """ - def delete_member(%Member{} = member) do - Repo.delete(member) - end - - @doc """ - Returns the list of administrator members for a group. - """ - def list_administrator_members_for_group(id, page \\ nil, limit \\ nil) do - Repo.all( - from( - m in Member, - where: m.parent_id == ^id and (m.role == ^:creator or m.role == ^:administrator), - preload: [:actor] - ) - |> Page.paginate(page, limit) - ) - end - - @doc """ - Get all group ids where the actor_id is the last administrator - """ - def list_group_id_where_last_administrator(actor_id) do - in_query = - from( - m in Member, - where: m.actor_id == ^actor_id and (m.role == ^:creator or m.role == ^:administrator), - select: m.parent_id - ) - - Repo.all( - from( - m in Member, - where: m.role == ^:creator or m.role == ^:administrator, - join: m2 in subquery(in_query), - on: m.parent_id == m2.parent_id, - group_by: m.parent_id, - select: m.parent_id, - having: count(m.actor_id) == 1 - ) - ) - end - - @doc """ - Returns the memberships for an actor - """ - @spec groups_memberships_for_actor(Actor.t()) :: list(Member.t()) - def groups_memberships_for_actor(%Actor{id: id} = _actor) do - Repo.all( - from( - m in Member, - where: m.actor_id == ^id, - preload: [:parent] - ) - ) - end - - @doc """ - Returns the memberships for a group - """ - @spec memberships_for_group(Actor.t()) :: list(Member.t()) - def memberships_for_group(%Actor{type: :Group, id: id} = _group) do - Repo.all( - from( - m in Member, - where: m.parent_id == ^id, - preload: [:parent, :actor] - ) - ) - end - - alias Mobilizon.Actors.Bot - - @doc """ - Returns the list of bots. - - ## Examples - - iex> list_bots() - [%Mobilizon.Actors.Bot{}] - - """ - def list_bots do - Repo.all(Bot) - end - - @doc """ - Gets a single bot. - - Raises `Ecto.NoResultsError` if the Bot does not exist. - - ## Examples - - iex> get_bot!(123) - %Mobilizon.Actors.Bot{} - - iex> get_bot!(456) - ** (Ecto.NoResultsError) - - """ - def get_bot!(id), do: Repo.get!(Bot, id) - - @doc """ - Get the bot associated to an actor - """ - @spec get_bot_by_actor(Actor.t()) :: Bot.t() - def get_bot_by_actor(%Actor{} = actor) do - Repo.get_by!(Bot, actor_id: actor.id) - end - - @doc """ - Creates a bot. - - ## Examples - - iex> create_bot(%{source: "toto"}) - {:ok, %Mobilizon.Actors.Bot{}} - - iex> create_bot(%{source: nil}) - {:error, %Ecto.Changeset{}} - - """ - def create_bot(attrs \\ %{}) do - %Bot{} - |> Bot.changeset(attrs) - |> Repo.insert() - end - - @doc """ - Updates a bot. - - ## Examples - - iex> update_bot(%Bot{}, %{source: "new"}) - {:ok, %Mobilizon.Actors.Bot{}} - - iex> update_bot(%Bot{}, %{source: nil}) - {:error, %Ecto.Changeset{}} - - """ - def update_bot(%Bot{} = bot, attrs) do - bot - |> Bot.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Bot. - - ## Examples - - iex> delete_bot(%Bot{}) - {:ok, %Mobilizon.Actors.Bot{}} - - iex> delete_bot(%Bot{}) - {:error, %Ecto.Changeset{}} - - """ - def delete_bot(%Bot{} = bot) do - Repo.delete(bot) - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking bot changes. - - ## Examples - - iex> change_bot(%Bot{}) - %Ecto.Changeset{data: %Mobilizon.Actors.Bot{}} - - """ - def change_bot(%Bot{} = bot) do - Bot.changeset(bot, %{}) - end - - @doc """ - Gets a single follower. - - Raises `Ecto.NoResultsError` if the Follower does not exist. - - ## Examples - - iex> get_follower!(123) - %Mobilizon.Actors.Follower{} - - iex> get_follower!(456) - ** (Ecto.NoResultsError) - - """ - def get_follower!(id) do - Repo.get!(Follower, id) - |> Repo.preload([:actor, :target_actor]) - end - - @doc """ - Get a follow by the followed actor and following actor - """ - @spec get_follower(Actor.t(), Actor.t()) :: Follower.t() - def get_follower(%Actor{id: followed_id}, %Actor{id: follower_id}) do - Repo.one( - from(f in Follower, where: f.target_actor_id == ^followed_id and f.actor_id == ^follower_id) - ) - end - - @doc """ - Get a follow by the followed actor and following actor - """ - @spec get_follow_by_url(String.t()) :: Follower.t() - def get_follow_by_url(url) do - Repo.one( - from(f in Follower, - where: f.url == ^url, - preload: [:actor, :target_actor] - ) - ) - end - - @doc """ - Get followers from an actor - - If actor A and C both follow actor B, actor B's followers are A and C - """ - @spec get_followers(struct(), number(), number()) :: map() - def get_followers(%Actor{id: actor_id} = _actor, page \\ nil, limit \\ nil) do - query = - from( - a in Actor, - join: f in Follower, - on: a.id == f.actor_id, - where: f.target_actor_id == ^actor_id - ) - - total = Task.async(fn -> Repo.aggregate(query, :count, :id) end) - elements = Task.async(fn -> Repo.all(Page.paginate(query, page, limit)) end) - - %{total: Task.await(total), elements: Task.await(elements)} - end - - @spec get_full_followers(struct()) :: list() - def get_full_followers(%Actor{} = actor) do - actor - |> get_full_followers_query() - |> Repo.all() - end - - @spec get_full_external_followers(struct()) :: list() - def get_full_external_followers(%Actor{} = actor) do - actor - |> get_full_followers_query() - |> where([a], not is_nil(a.domain)) - |> Repo.all() - end - - @doc """ - Get followings from an actor - - If actor A follows actor B and C, actor A's followings are B and B - """ - @spec get_followings(struct(), number(), number()) :: list() - def get_followings(%Actor{id: actor_id} = _actor, page \\ nil, limit \\ nil) do - query = - from( - a in Actor, - join: f in Follower, - on: a.id == f.target_actor_id, - where: f.actor_id == ^actor_id - ) - - total = Task.async(fn -> Repo.aggregate(query, :count, :id) end) - elements = Task.async(fn -> Repo.all(Page.paginate(query, page, limit)) end) - - %{total: Task.await(total), elements: Task.await(elements)} - end - - @spec get_full_followings(struct()) :: list() - def get_full_followings(%Actor{id: actor_id} = _actor) do - Repo.all( - from( - a in Actor, - join: f in Follower, - on: a.id == f.target_actor_id, - where: f.actor_id == ^actor_id - ) - ) - end - - defp get_full_followers_query(%Actor{id: actor_id} = _actor) do - from( - a in Actor, - join: f in Follower, - on: a.id == f.actor_id, - where: f.target_actor_id == ^actor_id - ) - end - - @doc """ - Creates a follower. - - ## Examples - - iex> create_follower(%{actor: %Actor{}}) - {:ok, %Mobilizon.Actors.Follower{}} - - iex> create_follower(%{actor: nil}) - {:error, %Ecto.Changeset{}} - - """ - def create_follower(attrs \\ %{}) do - with {:ok, %Follower{} = follower} <- - %Follower{} - |> Follower.changeset(attrs) - |> Repo.insert() do - {:ok, Repo.preload(follower, [:actor, :target_actor])} - end - end - - @doc """ - Updates a follower. - - ## Examples - - iex> update_follower(Follower{}, %{approved: true}) - {:ok, %Mobilizon.Actors.Follower{}} - - iex> update_follower(Follower{}, %{approved: nil}) - {:error, %Ecto.Changeset{}} - - """ - def update_follower(%Follower{} = follower, attrs) do - follower - |> Follower.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Follower. - - ## Examples - - iex> delete_follower(Follower{}) - {:ok, %Mobilizon.Actors.Follower{}} - - iex> delete_follower(Follower{}) - {:error, %Ecto.Changeset{}} - - """ - def delete_follower(%Follower{} = follower) do - Repo.delete(follower) - end - - @doc """ - Delete a follower by followed and follower actors - - ## Examples - - iex> delete_follower(%Actor{}, %Actor{}) - {:ok, %Mobilizon.Actors.Follower{}} - - iex> delete_follower(%Actor{}, %Actor{}) - {:error, %Ecto.Changeset{}} - - """ - @spec delete_follower(Actor.t(), Actor.t()) :: - {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} - def delete_follower(%Actor{} = followed, %Actor{} = follower) do - get_follower(followed, follower) |> Repo.delete() - end - - @doc """ - Make an actor follow another - """ - @spec follow(struct(), struct(), boolean()) :: Follower.t() | {:error, String.t()} - def follow(%Actor{} = followed, %Actor{} = follower, url \\ nil, approved \\ true) do - with {:suspended, false} <- {:suspended, followed.suspended}, - # Check if followed has blocked follower - {:already_following, false} <- {:already_following, following?(follower, followed)} do - do_follow(follower, followed, approved, url) - else - {:already_following, %Follower{}} -> - {:error, :already_following, - "Could not follow actor: you are already following #{followed.preferred_username}"} - - {:suspended, _} -> - {:error, :suspended, - "Could not follow actor: #{followed.preferred_username} has been suspended"} - end - end - - @doc """ - Unfollow an actor (remove a `Mobilizon.Actors.Follower`) - """ - @spec unfollow(struct(), struct()) :: {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} - def unfollow(%Actor{} = followed, %Actor{} = follower) do - case {:already_following, following?(follower, followed)} do - {:already_following, %Follower{} = follow} -> - delete_follower(follow) - - {:already_following, false} -> - {:error, "Could not unfollow actor: you are not following #{followed.preferred_username}"} - end - end - - @spec do_follow(struct(), struct(), boolean(), String.t()) :: - {:ok, Follower.t()} | {:error, Ecto.Changeset.t()} - defp do_follow(%Actor{} = follower, %Actor{} = followed, approved, url) do - Logger.info( - "Making #{follower.preferred_username} follow #{followed.preferred_username} (approved: #{ - approved - })" - ) - - create_follower(%{ - "actor_id" => follower.id, - "target_actor_id" => followed.id, - "approved" => approved, - "url" => url - }) - end - - @doc """ - Returns whether an actor is following another - """ - @spec following?(struct(), struct()) :: Follower.t() | false - def following?( - %Actor{} = follower_actor, - %Actor{} = followed_actor - ) do - case get_follower(followed_actor, follower_actor) do - nil -> false - %Follower{} = follow -> follow - end - end - - defp remove_banner(%Actor{banner: nil} = actor), do: {:ok, actor} - - defp remove_banner(%Actor{banner: %File{url: url}} = actor) do - safe_remove_file(url, actor) - end - - defp remove_avatar(%Actor{avatar: nil} = actor), do: {:ok, actor} - - defp remove_avatar(%Actor{avatar: %File{url: url}} = actor) do - safe_remove_file(url, actor) - end - - defp safe_remove_file(url, %Actor{} = actor) do - case MobilizonWeb.Upload.remove(url) do - {:ok, _value} -> - {:ok, actor} - - {:error, error} -> - Logger.error("Error while removing an upload file") - Logger.debug(inspect(error)) - {:ok, actor} - end - end - - @spec delete_files_if_media_changed(Ecto.Changeset.t()) :: Ecto.Changeset.t() - defp delete_files_if_media_changed(%Ecto.Changeset{changes: changes, data: data} = changeset) do - Enum.each([:avatar, :banner], fn key -> - if Map.has_key?(changes, key) do - with %Ecto.Changeset{changes: %{url: new_url}} <- changes[key], - %{url: old_url} <- data |> Map.from_struct() |> Map.get(key), - false <- new_url == old_url do - MobilizonWeb.Upload.remove(old_url) - end - end - end) - - changeset - end end diff --git a/lib/mobilizon/actors/follower.ex b/lib/mobilizon/actors/follower.ex index b8497e96..0164d39a 100644 --- a/lib/mobilizon/actors/follower.ex +++ b/lib/mobilizon/actors/follower.ex @@ -44,8 +44,11 @@ defmodule Mobilizon.Actors.Follower do @spec ensure_url(Ecto.Changeset.t()) :: Ecto.Changeset.t() defp ensure_url(%Ecto.Changeset{data: %__MODULE__{url: nil}} = changeset) do case fetch_change(changeset, :url) do - {:ok, _url} -> changeset - :error -> generate_url(changeset) + {:ok, _url} -> + changeset + + :error -> + generate_url(changeset) end end diff --git a/lib/mobilizon/addresses/addresses.ex b/lib/mobilizon/addresses/addresses.ex index 7c8ac7b4..fdef1ca7 100644 --- a/lib/mobilizon/addresses/addresses.ex +++ b/lib/mobilizon/addresses/addresses.ex @@ -86,8 +86,11 @@ defmodule Mobilizon.Addresses do |> filter_by_contry(Keyword.get(options, :country)) case Keyword.get(options, :single, false) do - true -> Repo.one(query) - false -> Repo.all(query) + true -> + Repo.one(query) + + false -> + Repo.all(query) end end diff --git a/lib/mobilizon/media/media.ex b/lib/mobilizon/media/media.ex index 73533934..c1923443 100644 --- a/lib/mobilizon/media/media.ex +++ b/lib/mobilizon/media/media.ex @@ -75,8 +75,11 @@ defmodule Mobilizon.Media do |> Repo.transaction() case transaction do - {:ok, %{picture: %Picture{} = picture}} -> {:ok, picture} - {:error, :remove, error, _} -> {:error, error} + {:ok, %{picture: %Picture{} = picture}} -> + {:ok, picture} + + {:error, :remove, error, _} -> + {:error, error} end end diff --git a/lib/mobilizon/users/user.ex b/lib/mobilizon/users/user.ex index eb889510..b9da2002 100644 --- a/lib/mobilizon/users/user.ex +++ b/lib/mobilizon/users/user.ex @@ -154,8 +154,11 @@ defmodule Mobilizon.Users.User do case changeset do %Ecto.Changeset{valid?: true, changes: %{email: email}} -> case EmailChecker.valid?(email) do - false -> add_error(changeset, :email, "Email doesn't fit required format") - true -> changeset + false -> + add_error(changeset, :email, "Email doesn't fit required format") + + true -> + changeset end _ -> diff --git a/lib/mobilizon/users/users.ex b/lib/mobilizon/users/users.ex index 4fe5e806..1c3ddabb 100644 --- a/lib/mobilizon/users/users.ex +++ b/lib/mobilizon/users/users.ex @@ -59,8 +59,11 @@ defmodule Mobilizon.Users do query = user_by_email_query(email, activated) case Repo.one(query) do - nil -> {:error, :user_not_found} - user -> {:ok, user} + nil -> + {:error, :user_not_found} + + user -> + {:ok, user} end end @@ -147,8 +150,11 @@ defmodule Mobilizon.Users do case actor do nil -> case get_actors_for_user(user) do - [] -> nil - actors -> hd(actors) + [] -> + nil + + actors -> + hd(actors) end actor -> diff --git a/lib/mobilizon_web/api/follows.ex b/lib/mobilizon_web/api/follows.ex index a96b5c60..4cba7a5e 100644 --- a/lib/mobilizon_web/api/follows.ex +++ b/lib/mobilizon_web/api/follows.ex @@ -32,7 +32,7 @@ defmodule MobilizonWeb.API.Follows do def accept(%Actor{} = follower, %Actor{} = followed) do with %Follower{approved: false, id: follow_id, url: follow_url} = follow <- - Actors.following?(follower, followed), + Actors.is_following(follower, followed), activity_follow_url <- "#{MobilizonWeb.Endpoint.url()}/accept/follow/#{follow_id}", data <- ActivityPub.Utils.make_follow_data(followed, follower, follow_url), diff --git a/lib/mobilizon_web/resolvers/member.ex b/lib/mobilizon_web/resolvers/member.ex index b1243064..1cfd320a 100644 --- a/lib/mobilizon_web/resolvers/member.ex +++ b/lib/mobilizon_web/resolvers/member.ex @@ -9,7 +9,7 @@ defmodule MobilizonWeb.Resolvers.Member do Find members for group """ def find_members_for_group(%Actor{} = actor, _args, _resolution) do - members = Actors.memberships_for_group(actor) + members = Actors.list_members_for_group(actor) {:ok, members} end end diff --git a/lib/mobilizon_web/resolvers/person.ex b/lib/mobilizon_web/resolvers/person.ex index 8f751b7e..7c39d125 100644 --- a/lib/mobilizon_web/resolvers/person.ex +++ b/lib/mobilizon_web/resolvers/person.ex @@ -207,7 +207,7 @@ defmodule MobilizonWeb.Resolvers.Person do # We check that the actor is not the last administrator/creator of a group @spec last_admin_of_a_group?(integer()) :: boolean() defp last_admin_of_a_group?(actor_id) do - length(Actors.list_group_id_where_last_administrator(actor_id)) > 0 + length(Actors.list_group_ids_where_last_administrator(actor_id)) > 0 end @spec proxify_avatar(Actor.t()) :: Actor.t() diff --git a/lib/mobilizon_web/views/activity_pub/actor_view.ex b/lib/mobilizon_web/views/activity_pub/actor_view.ex index 00dbd041..5ecf495c 100644 --- a/lib/mobilizon_web/views/activity_pub/actor_view.ex +++ b/lib/mobilizon_web/views/activity_pub/actor_view.ex @@ -49,7 +49,7 @@ defmodule MobilizonWeb.ActivityPub.ActorView do def render("following.json", %{actor: actor, page: page}) do %{total: total, elements: following} = if Actor.is_public_visibility(actor), - do: Actors.get_followings(actor, page), + do: Actors.build_followings_for_actor(actor, page), else: @private_visibility_empty_collection following @@ -60,7 +60,7 @@ defmodule MobilizonWeb.ActivityPub.ActorView do def render("following.json", %{actor: actor}) do %{total: total, elements: following} = if Actor.is_public_visibility(actor), - do: Actors.get_followings(actor), + do: Actors.build_followings_for_actor(actor), else: @private_visibility_empty_collection %{ @@ -75,7 +75,7 @@ defmodule MobilizonWeb.ActivityPub.ActorView do def render("followers.json", %{actor: actor, page: page}) do %{total: total, elements: followers} = if Actor.is_public_visibility(actor), - do: Actors.get_followers(actor, page), + do: Actors.build_followers_for_actor(actor, page), else: @private_visibility_empty_collection followers @@ -86,7 +86,7 @@ defmodule MobilizonWeb.ActivityPub.ActorView do def render("followers.json", %{actor: actor}) do %{total: total, elements: followers} = if Actor.is_public_visibility(actor), - do: Actors.get_followers(actor), + do: Actors.build_followers_for_actor(actor), else: @private_visibility_empty_collection %{ diff --git a/lib/service/activity_pub/activity_pub.ex b/lib/service/activity_pub/activity_pub.ex index c0733c66..507768db 100644 --- a/lib/service/activity_pub/activity_pub.ex +++ b/lib/service/activity_pub/activity_pub.ex @@ -551,7 +551,7 @@ defmodule Mobilizon.Service.ActivityPub do followers = if actor.followers_url in activity.recipients do - Actors.get_full_external_followers(actor) + Actors.list_external_followers_for_actor(actor) else [] end diff --git a/lib/service/activity_pub/relay.ex b/lib/service/activity_pub/relay.ex index 96e2bedb..3333be71 100644 --- a/lib/service/activity_pub/relay.ex +++ b/lib/service/activity_pub/relay.ex @@ -17,7 +17,7 @@ defmodule Mobilizon.Service.ActivityPub.Relay do def get_actor do with {:ok, %Actor{} = actor} <- - Actors.get_or_create_service_actor_by_url("#{MobilizonWeb.Endpoint.url()}/relay") do + Actors.get_or_create_actor_by_url("#{MobilizonWeb.Endpoint.url()}/relay") do actor end end diff --git a/lib/service/activity_pub/transmogrifier.ex b/lib/service/activity_pub/transmogrifier.ex index a94548aa..41d81c24 100644 --- a/lib/service/activity_pub/transmogrifier.ex +++ b/lib/service/activity_pub/transmogrifier.ex @@ -642,7 +642,7 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do defp get_follow(follow_object) do with follow_object_id when not is_nil(follow_object_id) <- Utils.get_url(follow_object), {:not_found, %Follower{} = follow} <- - {:not_found, Actors.get_follow_by_url(follow_object_id)} do + {:not_found, Actors.get_follower_by_url(follow_object_id)} do {:ok, follow} else {:not_found, _err} -> diff --git a/test/mobilizon/actors/actors_test.exs b/test/mobilizon/actors/actors_test.exs index 7c211f47..df5a5755 100644 --- a/test/mobilizon/actors/actors_test.exs +++ b/test/mobilizon/actors/actors_test.exs @@ -166,11 +166,11 @@ defmodule Mobilizon.ActorsTest do end end - test "test get_local_actor_by_username/1 returns local actors with similar usernames", %{ + test "test list_local_actor_by_username/1 returns local actors with similar usernames", %{ actor: actor } do actor2 = insert(:actor, preferred_username: "tcit") - [%Actor{id: actor_found_id} | tail] = Actors.get_local_actor_by_username("tcit") + [%Actor{id: actor_found_id} | tail] = Actors.list_local_actor_by_username("tcit") %Actor{id: actor2_found_id} = hd(tail) assert MapSet.new([actor_found_id, actor2_found_id]) == MapSet.new([actor.id, actor2.id]) end @@ -416,11 +416,6 @@ defmodule Mobilizon.ActorsTest do assert {:ok, %Bot{}} = Actors.delete_bot(bot) assert_raise Ecto.NoResultsError, fn -> Actors.get_bot!(bot.id) end end - - test "change_bot/1 returns a bot changeset" do - bot = insert(:bot) - assert %Ecto.Changeset{} = Actors.change_bot(bot) - end end describe "followers" do @@ -458,8 +453,8 @@ defmodule Mobilizon.ActorsTest do assert {:ok, %Follower{} = follower} = Actors.create_follower(valid_attrs) assert follower.approved == true - assert %{total: 1, elements: [target_actor]} = Actors.get_followings(actor) - assert %{total: 1, elements: [actor]} = Actors.get_followers(target_actor) + assert %{total: 1, elements: [target_actor]} = Actors.build_followings_for_actor(actor) + assert %{total: 1, elements: [actor]} = Actors.build_followers_for_actor(target_actor) end test "create_follower/1 with valid data but same actors fails to create a follower", %{ @@ -568,8 +563,8 @@ defmodule Mobilizon.ActorsTest do assert {:ok, %Member{} = member} = Actors.create_member(valid_attrs) assert member.role == :member - assert [group] = Actors.get_groups_member_of(actor) - assert [actor] = Actors.get_members_for_group(group) + assert [group] = Actors.list_groups_member_of(actor) + assert [actor] = Actors.list_members_for_group(group) end test "create_member/1 with valid data but same actors fails to create a member", %{ diff --git a/test/mobilizon/service/activity_pub/transmogrifier_test.exs b/test/mobilizon/service/activity_pub/transmogrifier_test.exs index 1c515c6b..7eb340ae 100644 --- a/test/mobilizon/service/activity_pub/transmogrifier_test.exs +++ b/test/mobilizon/service/activity_pub/transmogrifier_test.exs @@ -223,7 +223,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do assert data["id"] == "https://social.tcit.fr/users/tcit#follows/2" actor = Actors.get_actor_with_preload(actor.id) - assert Actors.following?(Actors.get_actor_by_url!(data["actor"], true), actor) + assert Actors.is_following(Actors.get_actor_by_url!(data["actor"], true), actor) end # test "it works for incoming follow requests from hubzilla" do @@ -240,7 +240,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do # assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" # assert data["type"] == "Follow" # assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" - # assert User.following?(User.get_by_ap_id(data["actor"]), user) + # assert User.is_following(User.get_by_ap_id(data["actor"]), user) # end # test "it works for incoming likes" do @@ -498,7 +498,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do assert data["actor"] == "https://social.tcit.fr/users/tcit" {:ok, followed} = Actors.get_actor_by_url(data["actor"]) - refute Actors.following?(followed, actor) + refute Actors.is_following(followed, actor) end # test "it works for incoming blocks" do @@ -581,10 +581,10 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do follower = insert(:actor) followed = insert(:actor) - refute Actors.following?(follower, followed) + refute Actors.is_following(follower, followed) {:ok, follow_activity, _} = ActivityPub.follow(follower, followed) - assert Actors.following?(follower, followed) + assert Actors.is_following(follower, followed) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") @@ -605,7 +605,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, follower} = Actors.get_actor_by_url(follower.url) - assert Actors.following?(follower, followed) + assert Actors.is_following(follower, followed) end test "it works for incoming accepts which are referenced by IRI only" do @@ -627,7 +627,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, follower} = Actors.get_actor_by_url(follower.url) - assert Actors.following?(follower, followed) + assert Actors.is_following(follower, followed) end test "it fails for incoming accepts which cannot be correlated" do @@ -646,7 +646,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, follower} = Actors.get_actor_by_url(follower.url) - refute Actors.following?(follower, followed) + refute Actors.is_following(follower, followed) end test "it fails for incoming rejects which cannot be correlated" do @@ -665,7 +665,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, follower} = Actors.get_actor_by_url(follower.url) - refute Actors.following?(follower, followed) + refute Actors.is_following(follower, followed) end test "it works for incoming rejects which are referenced by IRI only" do @@ -674,7 +674,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, follow_activity, _} = ActivityPub.follow(follower, followed) - assert Actors.following?(follower, followed) + assert Actors.is_following(follower, followed) reject_data = File.read!("test/fixtures/mastodon-reject-activity.json") @@ -684,7 +684,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do {:ok, %Activity{data: _}, _} = Transmogrifier.handle_incoming(reject_data) - refute Actors.following?(follower, followed) + refute Actors.is_following(follower, followed) end test "it rejects activities without a valid ID" do diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index 22f67580..c93e8ad4 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -22,7 +22,7 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do {:ok, target_actor} = Actors.get_actor_by_url(target_instance) refute is_nil(target_actor.domain) - assert Actors.following?(local_actor, target_actor) + assert Actors.is_following(local_actor, target_actor) end end end @@ -36,11 +36,11 @@ defmodule Mix.Tasks.Mobilizon.RelayTest do %Actor{} = local_actor = Relay.get_actor() {:ok, %Actor{} = target_actor} = Actors.get_actor_by_url(target_instance) - assert %Follower{} = Actors.following?(local_actor, target_actor) + assert %Follower{} = Actors.is_following(local_actor, target_actor) Mix.Tasks.Mobilizon.Relay.run(["unfollow", target_instance]) - refute Actors.following?(local_actor, target_actor) + refute Actors.is_following(local_actor, target_actor) end end end