Various refactoring and typespec improvements

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2021-09-24 16:46:42 +02:00
parent d235653876
commit 1893d9f55b
142 changed files with 1854 additions and 1297 deletions

View File

@@ -11,7 +11,7 @@ defmodule Mobilizon.Actors.Actor do
alias Mobilizon.Actors.{ActorOpenness, ActorType, ActorVisibility, Follower, Member}
alias Mobilizon.Addresses.Address
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.{Event, FeedToken}
alias Mobilizon.Events.{Event, FeedToken, Participant}
alias Mobilizon.Medias.File
alias Mobilizon.Reports.{Note, Report}
alias Mobilizon.Users.User
@@ -33,8 +33,8 @@ defmodule Mobilizon.Actors.Actor do
posts_url: String.t(),
events_url: String.t(),
type: ActorType.t(),
name: String.t(),
domain: String.t(),
name: String.t() | nil,
domain: String.t() | nil,
summary: String.t(),
preferred_username: String.t(),
keys: String.t(),
@@ -42,12 +42,13 @@ defmodule Mobilizon.Actors.Actor do
openness: ActorOpenness.t(),
visibility: ActorVisibility.t(),
suspended: boolean,
avatar: File.t(),
banner: File.t(),
avatar: File.t() | nil,
banner: File.t() | nil,
user: User.t(),
followers: [Follower.t()],
followings: [Follower.t()],
organized_events: [Event.t()],
participations: [Participant.t()],
comments: [Comment.t()],
feed_tokens: [FeedToken.t()],
created_reports: [Report.t()],
@@ -184,6 +185,7 @@ defmodule Mobilizon.Actors.Actor do
has_many(:created_reports, Report, foreign_key: :reporter_id)
has_many(:subject_reports, Report, foreign_key: :reported_id)
has_many(:report_notes, Note, foreign_key: :moderator_id)
has_many(:participations, Participant, foreign_key: :actor_id)
has_many(:mentions, Mention)
has_many(:shares, Share, foreign_key: :actor_id)
has_many(:owner_shares, Share, foreign_key: :owner_actor_id)
@@ -243,7 +245,7 @@ defmodule Mobilizon.Actors.Actor do
end
@doc false
@spec changeset(t, map) :: Ecto.Changeset.t()
@spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = actor, attrs) do
actor
|> cast(attrs, @attrs)
@@ -278,7 +280,7 @@ defmodule Mobilizon.Actors.Actor do
@doc """
Changeset for person registration.
"""
@spec registration_changeset(t, map) :: Ecto.Changeset.t()
@spec registration_changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def registration_changeset(%__MODULE__{} = actor, attrs) do
actor
|> cast(attrs, @registration_attrs)
@@ -293,19 +295,14 @@ defmodule Mobilizon.Actors.Actor do
"""
@spec remote_actor_creation_changeset(map) :: Ecto.Changeset.t()
def remote_actor_creation_changeset(attrs) do
changeset =
%__MODULE__{}
|> cast(attrs, @remote_actor_creation_attrs)
|> validate_required(@remote_actor_creation_required_attrs)
|> common_changeset(attrs)
|> unique_username_validator()
|> validate_required(:domain)
|> validate_length(:summary, max: 5000)
|> validate_length(:preferred_username, max: 100)
Logger.debug("Remote actor creation: #{inspect(changeset)}")
changeset
%__MODULE__{}
|> cast(attrs, @remote_actor_creation_attrs)
|> validate_required(@remote_actor_creation_required_attrs)
|> common_changeset(attrs)
|> unique_username_validator()
|> validate_required(:domain)
|> validate_length(:summary, max: 5000)
|> validate_length(:preferred_username, max: 100)
end
@spec common_changeset(Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
@@ -323,7 +320,7 @@ defmodule Mobilizon.Actors.Actor do
@doc """
Changeset for group creation
"""
@spec group_creation_changeset(t, map) :: Ecto.Changeset.t()
@spec group_creation_changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def group_creation_changeset(actor, params) do
actor
|> cast(params, @group_creation_attrs)
@@ -416,12 +413,8 @@ defmodule Mobilizon.Actors.Actor do
@spec build_relay_creation_attrs :: Ecto.Changeset.t()
def build_relay_creation_attrs do
data = %{
name: Config.get([:instance, :name], "Mobilizon"),
summary:
Config.get(
[:instance, :description],
"An internal service actor for this Mobilizon instance"
),
name: Config.instance_name(),
summary: Config.instance_description(),
keys: Crypto.generate_rsa_2048_private_key(),
preferred_username: "relay",
domain: nil,

View File

@@ -75,13 +75,20 @@ defmodule Mobilizon.Actors do
@doc """
Gets an actor with preloaded relations.
"""
@spec get_actor_with_preload(integer | String.t()) :: Actor.t() | nil
@spec get_actor_with_preload(integer | String.t(), boolean) :: Actor.t() | nil
def get_actor_with_preload(id, include_suspended \\ false) do
id
|> actor_with_preload_query(include_suspended)
|> Repo.one()
end
@spec get_actor_with_preload!(integer | String.t(), boolean) :: Actor.t()
def get_actor_with_preload!(id, include_suspended \\ false) do
id
|> actor_with_preload_query(include_suspended)
|> Repo.one!()
end
@doc """
Gets a local actor with preloaded relations.
"""
@@ -148,9 +155,7 @@ defmodule Mobilizon.Actors do
"""
@spec get_actor_by_name(String.t(), ActorType.t() | nil) :: Actor.t() | nil
def get_actor_by_name(name, type \\ nil) do
query = from(a in Actor)
query
Actor
|> filter_by_type(type)
|> filter_by_name(name |> String.trim() |> String.trim_leading("@") |> String.split("@"))
|> Repo.one()
@@ -161,9 +166,7 @@ defmodule Mobilizon.Actors do
"""
@spec get_local_actor_by_name(String.t()) :: Actor.t() | nil
def get_local_actor_by_name(name) do
query = from(a in Actor)
query
Actor
|> filter_by_name([name])
|> Repo.one()
end
@@ -210,7 +213,8 @@ defmodule Mobilizon.Actors do
@doc """
Creates a new person actor.
"""
@spec new_person(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
@spec new_person(map, default_actor :: boolean()) ::
{:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def new_person(args, default_actor \\ false) do
args = Map.put(args, :keys, Crypto.generate_rsa_2048_private_key())
@@ -323,8 +327,8 @@ defmodule Mobilizon.Actors do
String.t(),
boolean,
boolean,
integer,
integer
integer | nil,
integer | nil
) :: Page.t()
def list_actors(
type \\ :Person,
@@ -367,7 +371,23 @@ defmodule Mobilizon.Actors do
|> Page.build_page(page, limit)
end
@spec filter_actors(Ecto.Query.t(), String.t(), String.t(), String.t(), boolean(), boolean()) ::
@spec list_suspended_actors_to_purge(Keyword.t()) :: list(Actors.t())
def list_suspended_actors_to_purge(options) do
suspension_days = Keyword.get(options, :suspension, 30)
Actor
|> filter_suspended_days(suspension_days)
|> Repo.all()
end
@spec filter_actors(
Ecto.Queryable.t(),
String.t(),
String.t(),
String.t(),
boolean(),
boolean()
) ::
Ecto.Query.t()
defp filter_actors(
query,
@@ -403,14 +423,27 @@ defmodule Mobilizon.Actors do
defp filter_remote(query, true), do: filter_local(query)
defp filter_remote(query, false), do: filter_external(query)
@spec filter_suspended(Ecto.Query.t(), boolean()) :: Ecto.Query.t()
@spec filter_suspended(Ecto.Queryable.t(), boolean()) :: Ecto.Query.t()
defp filter_suspended(query, true), do: where(query, [a], a.suspended)
defp filter_suspended(query, false), do: where(query, [a], not a.suspended)
@spec filter_out_anonymous_actor_id(Ecto.Query.t(), integer() | String.t()) :: Ecto.Query.t()
@spec filter_out_anonymous_actor_id(Ecto.Queryable.t(), integer() | String.t()) ::
Ecto.Query.t()
defp filter_out_anonymous_actor_id(query, anonymous_actor_id),
do: where(query, [a], a.id != ^anonymous_actor_id)
@spec filter_suspended_days(Ecto.Queryable.t(), integer()) :: Ecto.Query.t()
defp filter_suspended_days(query, suspended_days) do
expiration_date = DateTime.add(DateTime.utc_now(), suspended_days * 24 * -3600)
where(
query,
[a],
a.suspended and
a.updated_at > ^expiration_date
)
end
@doc """
Returns the list of local actors by their username.
"""
@@ -486,14 +519,14 @@ defmodule Mobilizon.Actors do
end
end
@spec get_local_group_by_url(String.t()) :: Actor.t()
@spec get_local_group_by_url(String.t()) :: Actor.t() | nil
def get_local_group_by_url(group_url) do
group_query()
|> where([q], q.url == ^group_url and is_nil(q.domain))
|> Repo.one()
end
@spec get_group_by_members_url(String.t()) :: Actor.t()
@spec get_group_by_members_url(String.t()) :: Actor.t() | nil
def get_group_by_members_url(members_url) do
group_query()
|> where([q], q.members_url == ^members_url)
@@ -531,7 +564,7 @@ defmodule Mobilizon.Actors do
{:ok, %{insert_group: %Actor{} = group, add_admin_member: %Member{} = _admin_member}} ->
{:ok, group}
{:error, %Ecto.Changeset{} = err} ->
{:error, _err, %Ecto.Changeset{} = err, _} ->
{:error, err}
end
else
@@ -606,7 +639,7 @@ defmodule Mobilizon.Actors do
@doc """
Gets a single member.
"""
@spec get_member(integer | String.t()) :: {:ok, Member.t()} | nil
@spec get_member(integer | String.t()) :: Member.t() | nil
def get_member(id) do
Member
|> Repo.get(id)
@@ -623,7 +656,7 @@ defmodule Mobilizon.Actors do
@doc """
Gets a single member of an actor (for example a group).
"""
@spec get_member(integer | String.t(), integer | String.t()) ::
@spec get_member(actor_id :: integer | String.t(), parent_id :: 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
@@ -1190,31 +1223,35 @@ defmodule Mobilizon.Actors do
@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()}
@spec follow(
followed :: Actor.t(),
follower :: Actor.t(),
url :: String.t() | nil,
approved :: boolean | nil
) ::
{:ok, Follower.t()}
| {:error, :already_following | :followed_suspended | Ecto.Changeset.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 #{Actor.preferred_username_and_domain(follower)} follow #{Actor.preferred_username_and_domain(followed)} " <>
"(approved: #{approved})"
)
create_follower(%{
"actor_id" => follower.id,
"target_actor_id" => followed.id,
"approved" => approved,
"url" => url
})
if followed.suspended do
{:error, :followed_suspended}
else
{:already_following, %Follower{}} ->
{:error, :already_following,
"Could not follow actor: you are already following #{Actor.preferred_username_and_domain(followed)}"}
case is_following(follower, followed) do
%Follower{} ->
{:error, :already_following}
{:suspended, _} ->
{:error, :suspended,
"Could not follow actor: #{Actor.preferred_username_and_domain(followed)} has been suspended"}
nil ->
Logger.info(
"Making #{Actor.preferred_username_and_domain(follower)} follow #{Actor.preferred_username_and_domain(followed)} " <>
"(approved: #{approved})"
)
create_follower(%{
"actor_id" => follower.id,
"target_actor_id" => followed.id,
"approved" => approved,
"url" => url
})
end
end
end
@@ -1331,7 +1368,7 @@ defmodule Mobilizon.Actors do
)
end
@spec actor_by_username_or_name_query(Ecto.Query.t(), String.t()) :: Ecto.Query.t()
@spec actor_by_username_or_name_query(Ecto.Queryable.t(), String.t()) :: Ecto.Query.t()
defp actor_by_username_or_name_query(query, ""), do: query
defp actor_by_username_or_name_query(query, username) do
@@ -1358,7 +1395,7 @@ defmodule Mobilizon.Actors do
)
end
@spec actors_for_location(Ecto.Query.t(), String.t(), integer()) :: Ecto.Query.t()
@spec actors_for_location(Ecto.Queryable.t(), String.t(), integer()) :: Ecto.Query.t()
defp actors_for_location(query, location, radius)
when is_valid_string(location) and not is_nil(radius) do
with {lon, lat} <- Geohax.decode(location),
@@ -1474,7 +1511,7 @@ defmodule Mobilizon.Actors do
|> select([m, _a], m)
end
@spec filter_member_role(Ecto.Query.t(), list(atom()) | atom()) :: Ecto.Query.t()
@spec filter_member_role(Ecto.Queryable.t(), list(atom()) | atom()) :: Ecto.Query.t()
defp filter_member_role(query, []), do: query
defp filter_member_role(query, roles) when is_list(roles) do
@@ -1597,24 +1634,24 @@ defmodule Mobilizon.Actors do
|> order_by(desc: :updated_at)
end
@spec filter_local(Ecto.Query.t()) :: Ecto.Query.t()
@spec filter_local(Ecto.Queryable.t()) :: Ecto.Query.t()
defp filter_local(query) do
from(a in query, where: is_nil(a.domain))
end
@spec filter_external(Ecto.Query.t()) :: Ecto.Query.t()
@spec filter_external(Ecto.Queryable.t()) :: Ecto.Query.t()
defp filter_external(query) do
from(a in query, where: not is_nil(a.domain))
end
@spec filter_follower_actors_external(Ecto.Query.t()) :: Ecto.Query.t()
@spec filter_follower_actors_external(Ecto.Queryable.t()) :: Ecto.Query.t()
defp filter_follower_actors_external(query) do
query
|> where([_f, a], not is_nil(a.domain))
|> preload([f, a], [:target_actor, :actor])
end
@spec filter_by_type(Ecto.Query.t(), ActorType.t()) :: Ecto.Query.t()
@spec filter_by_type(Ecto.Queryable.t(), ActorType.t() | nil) :: Ecto.Queryable.t()
defp filter_by_type(query, type)
when type in [:Person, :Group, :Application, :Service, :Organisation] do
from(a in query, where: a.type == ^type)
@@ -1622,12 +1659,12 @@ defmodule Mobilizon.Actors do
defp filter_by_type(query, _type), do: query
@spec filter_by_types(Ecto.Query.t(), [ActorType.t()]) :: Ecto.Query.t()
@spec filter_by_types(Ecto.Queryable.t(), [ActorType.t()]) :: Ecto.Query.t()
defp filter_by_types(query, types) do
from(a in query, where: a.type in ^types)
end
@spec filter_by_minimum_visibility(Ecto.Query.t(), atom()) :: Ecto.Query.t()
@spec filter_by_minimum_visibility(Ecto.Queryable.t(), atom()) :: Ecto.Query.t()
defp filter_by_minimum_visibility(query, :private), do: query
defp filter_by_minimum_visibility(query, :restricted) do
@@ -1642,7 +1679,7 @@ defmodule Mobilizon.Actors do
from(a in query, where: a.visibility == ^:public)
end
@spec filter_by_name(query :: Ecto.Query.t(), [String.t()]) :: Ecto.Query.t()
@spec filter_by_name(query :: Ecto.Queryable.t(), [String.t()]) :: Ecto.Query.t()
defp filter_by_name(query, [name]) do
where(query, [a], a.preferred_username == ^name and is_nil(a.domain))
end
@@ -1655,7 +1692,7 @@ defmodule Mobilizon.Actors do
end
end
@spec filter_followed_by_approved_status(Ecto.Query.t(), boolean() | nil) :: Ecto.Query.t()
@spec filter_followed_by_approved_status(Ecto.Queryable.t(), boolean() | nil) :: Ecto.Query.t()
defp filter_followed_by_approved_status(query, nil), do: query
defp filter_followed_by_approved_status(query, approved) do

View File

@@ -32,7 +32,7 @@ defmodule Mobilizon.Actors.Bot do
end
@doc false
@spec changeset(t, map) :: Ecto.Changeset.t()
@spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = bot, attrs) do
bot
|> cast(attrs, @attrs)

View File

@@ -38,7 +38,7 @@ defmodule Mobilizon.Actors.Follower do
end
@doc false
@spec changeset(follower :: t, attrs :: map) :: Ecto.Changeset.t()
@spec changeset(follower :: t | Ecto.Schema.t(), attrs :: map) :: Ecto.Changeset.t()
def changeset(follower, attrs) do
follower
|> cast(attrs, @attrs)

View File

@@ -59,7 +59,7 @@ defmodule Mobilizon.Actors.Member do
def is_administrator(%__MODULE__{}), do: false
@doc false
@spec changeset(t, map) :: Ecto.Changeset.t()
@spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = member, attrs) do
member
|> cast(attrs, @attrs)