Spec fixes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-10-05 17:43:45 +02:00
parent 76bc409f68
commit dee7c58449
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
33 changed files with 193 additions and 234 deletions

View File

@ -30,16 +30,10 @@ defmodule Mobilizon.Federation.ActivityPub.Actions.Reject do
:invite -> reject_invite(entity, additional) :invite -> reject_invite(entity, additional)
end end
with {:ok, activity} <- create_activity(update_data, local), {:ok, activity} = create_activity(update_data, local)
:ok <- maybe_federate(activity), maybe_federate(activity)
:ok <- maybe_relay_if_group_activity(activity) do maybe_relay_if_group_activity(activity)
{:ok, activity, entity} {:ok, activity, entity}
else
err ->
Logger.error("Something went wrong while creating an activity")
Logger.debug(inspect(err))
err
end
end end
@spec reject_join(Participant.t(), map()) :: {:ok, Participant.t(), Activity.t()} | any() @spec reject_join(Participant.t(), map()) :: {:ok, Participant.t(), Activity.t()} | any()

View File

@ -1016,6 +1016,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
# Comment and conversations have different attributes for actor and groups # Comment and conversations have different attributes for actor and groups
@spec transform_object_data_for_discussion(map()) :: map()
defp transform_object_data_for_discussion(object_data) do defp transform_object_data_for_discussion(object_data) do
# Basic comment # Basic comment
if is_data_a_discussion_initialization?(object_data) do if is_data_a_discussion_initialization?(object_data) do

View File

@ -176,7 +176,8 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
:ok :ok
end end
@spec do_maybe_relay_if_group_activity(map(), list(String.t()) | String.t()) :: :ok # TODO: Is this a map or a String?
@spec do_maybe_relay_if_group_activity(map() | String.t(), list(String.t()) | String.t()) :: :ok
defp do_maybe_relay_if_group_activity(object, attributed_to) when is_list(attributed_to), defp do_maybe_relay_if_group_activity(object, attributed_to) when is_list(attributed_to),
do: do_maybe_relay_if_group_activity(object, hd(attributed_to)) do: do_maybe_relay_if_group_activity(object, hd(attributed_to))

View File

@ -68,6 +68,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Discussion do
%{actor_id: actor_id, creator_id: creator_id} %{actor_id: actor_id, creator_id: creator_id}
else else
{:error, error} -> {:error, error} {:error, error} -> {:error, error}
{:ok, %Actor{url: ^creator_url}} -> {:error, :creator_suspended}
{:ok, %Actor{url: ^actor_url}} -> {:error, :actor_suspended}
end end
end end
end end

View File

@ -11,7 +11,9 @@ defmodule Mobilizon.GraphQL.API.Comments do
@doc """ @doc """
Create a comment Create a comment
""" """
@spec create_comment(map) :: {:ok, Activity.t(), Comment.t()} | any @spec create_comment(map) ::
{:ok, Activity.t(), Comment.t()}
| {:error, :entity_tombstoned | atom() | Ecto.Changeset.t()}
def create_comment(args) do def create_comment(args) do
args = extract_pictures_from_comment_body(args) args = extract_pictures_from_comment_body(args)
Actions.Create.create(:comment, args, true) Actions.Create.create(:comment, args, true)
@ -20,7 +22,8 @@ defmodule Mobilizon.GraphQL.API.Comments do
@doc """ @doc """
Updates a comment Updates a comment
""" """
@spec update_comment(Comment.t(), map()) :: {:ok, Activity.t(), Comment.t()} | any @spec update_comment(Comment.t(), map()) ::
{:ok, Activity.t(), Comment.t()} | {:error, atom() | Ecto.Changeset.t()}
def update_comment(%Comment{} = comment, args) do def update_comment(%Comment{} = comment, args) do
args = extract_pictures_from_comment_body(args) args = extract_pictures_from_comment_body(args)
Actions.Update.update(comment, args, true) Actions.Update.update(comment, args, true)
@ -37,7 +40,9 @@ defmodule Mobilizon.GraphQL.API.Comments do
@doc """ @doc """
Creates a discussion (or reply to a discussion) Creates a discussion (or reply to a discussion)
""" """
@spec create_discussion(map()) :: map() @spec create_discussion(map()) ::
{:ok, Activity.t(), Discussion.t()}
| {:error, :entity_tombstoned | atom | Ecto.Changeset.t()}
def create_discussion(args) do def create_discussion(args) do
args = extract_pictures_from_comment_body(args) args = extract_pictures_from_comment_body(args)

View File

@ -20,7 +20,7 @@ defmodule Mobilizon.GraphQL.Error do
# Error Tuples # Error Tuples
# ------------ # ------------
# Regular errors # Regular errors
@spec normalize(error | list(error) | String.t() | any()) :: t() @spec normalize(any()) :: t() | list(t())
def normalize({:error, reason}) do def normalize({:error, reason}) do
handle(reason) handle(reason)
end end

View File

@ -106,7 +106,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
case Discussions.get_comment_with_preload(comment_id) do case Discussions.get_comment_with_preload(comment_id) do
%CommentModel{deleted_at: nil} = comment -> %CommentModel{deleted_at: nil} = comment ->
cond do cond do
{:comment_can_be_managed, true} == CommentModel.can_be_managed_by(comment, actor_id) -> {:comment_can_be_managed, true} ==
{:comment_can_be_managed, CommentModel.can_be_managed_by?(comment, actor_id)} ->
do_delete_comment(comment, actor) do_delete_comment(comment, actor)
role in [:moderator, :administrator] -> role in [:moderator, :administrator] ->

View File

@ -107,28 +107,29 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do
} }
} = _resolution } = _resolution
) do ) do
with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, if Actors.is_member?(actor_id, group_id) do
parent <- get_eventual_parent(args), parent = get_eventual_parent(args)
{:own_check, true} <- {:own_check, check_resource_owned_by_group(parent, group_id)},
{:ok, _, %Resource{} = resource} <-
Actions.Create.create(
:resource,
args
|> Map.put(:actor_id, group_id)
|> Map.put(:creator_id, actor_id),
true,
%{}
) do
{:ok, resource}
else
{:error, _} ->
{:error, dgettext("errors", "Error while creating resource")}
{:own_check, _} -> if check_resource_owned_by_group(parent, group_id) do
case Actions.Create.create(
:resource,
args
|> Map.put(:actor_id, group_id)
|> Map.put(:creator_id, actor_id),
true,
%{}
) do
{:ok, _, %Resource{} = resource} ->
{:ok, resource}
{:error, _err} ->
{:error, dgettext("errors", "Error while creating resource")}
end
else
{:error, dgettext("errors", "Parent resource doesn't belong to this group")} {:error, dgettext("errors", "Parent resource doesn't belong to this group")}
end
{:member, _} -> else
{:error, dgettext("errors", "Profile is not member of group")} {:error, dgettext("errors", "Profile is not member of group")}
end end
end end

View File

@ -54,7 +54,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Tag do
@doc """ @doc """
Retrieve the list of related tags for a parent tag Retrieve the list of related tags for a parent tag
""" """
@spec list_tags_for_post(Tag.t(), map(), Absinthe.Resolution.t()) :: {:ok, list(Tag.t())} @spec get_related_tags(Tag.t(), map(), Absinthe.Resolution.t()) :: {:ok, list(Tag.t())}
def get_related_tags(%Tag{} = tag, _args, _resolution) do def get_related_tags(%Tag{} = tag, _args, _resolution) do
{:ok, Events.list_tag_neighbors(tag)} {:ok, Events.list_tag_neighbors(tag)}
end end

View File

@ -425,35 +425,39 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
def change_email(_parent, %{email: new_email, password: password}, %{ def change_email(_parent, %{email: new_email, password: password}, %{
context: %{current_user: %User{email: old_email} = user} context: %{current_user: %User{email: old_email} = user}
}) do }) do
with {:can_change_password, true} <- if Authenticator.can_change_email?(user) do
{:can_change_password, Authenticator.can_change_email?(user)}, case Authenticator.login(old_email, password) do
{:current_password, {:ok, %User{}}} <- {:ok, %User{}} ->
{:current_password, Authenticator.login(user.email, password)}, if new_email != old_email do
{:same_email, false} <- {:same_email, new_email == old_email}, if Email.Checker.valid?(new_email) do
{:email_valid, true} <- {:email_valid, Email.Checker.valid?(new_email)}, case Users.update_user_email(user, new_email) do
{:ok, %User{} = user} <- Users.update_user_email(user, new_email) do {:ok, %User{} = user} ->
user user
|> Email.User.send_email_reset_old_email() |> Email.User.send_email_reset_old_email()
|> Email.Mailer.send_email_later() |> Email.Mailer.send_email_later()
user user
|> Email.User.send_email_reset_new_email() |> Email.User.send_email_reset_new_email()
|> Email.Mailer.send_email_later() |> Email.Mailer.send_email_later()
{:ok, user} {:ok, user}
{:error, %Ecto.Changeset{} = err} ->
Logger.debug(inspect(err))
{:error, dgettext("errors", "Failed to update user email")}
end
else
{:error, dgettext("errors", "The new email doesn't seem to be valid")}
end
else
{:error, dgettext("errors", "The new email must be different")}
end
{:error, _} ->
{:error, dgettext("errors", "The password provided is invalid")}
end
else else
{:current_password, {:error, _}} -> {:error, dgettext("errors", "User cannot change email")}
{:error, dgettext("errors", "The password provided is invalid")}
{:same_email, true} ->
{:error, dgettext("errors", "The new email must be different")}
{:email_valid, false} ->
{:error, dgettext("errors", "The new email doesn't seem to be valid")}
{:error, %Ecto.Changeset{} = err} ->
Logger.debug(inspect(err))
{:error, dgettext("errors", "Failed to update user email")}
end end
end end
@ -635,14 +639,14 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
user, user,
context context
) do ) do
with current_ip <- Map.get(context, :ip), current_ip = Map.get(context, :ip)
now <- DateTime.utc_now() do now = DateTime.utc_now()
Users.update_user(user, %{
last_sign_in_at: current_sign_in_at || now, Users.update_user(user, %{
last_sign_in_ip: current_sign_in_ip || current_ip, last_sign_in_at: current_sign_in_at || now,
current_sign_in_ip: current_ip, last_sign_in_ip: current_sign_in_ip || current_ip,
current_sign_in_at: now current_sign_in_ip: current_ip,
}) current_sign_in_at: now
end })
end end
end end

View File

@ -71,10 +71,7 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do
Actor #{preferred_username} refreshed Actor #{preferred_username} refreshed
""") """)
{:error, err} when is_binary(err) -> {:error, _err} ->
shell_error(err)
_err ->
shell_error("Error while refreshing actor #{preferred_username}") shell_error("Error while refreshing actor #{preferred_username}")
end end
end end

View File

@ -187,35 +187,29 @@ defmodule Mix.Tasks.Mobilizon.Instance do
end end
end end
@spec write_config(String.t(), String.t()) :: :ok | {:error, atom()}
defp write_config(config_path, result_config) do defp write_config(config_path, result_config) do
shell_info("Writing config to #{config_path}.") shell_info("Writing config to #{config_path}.")
case File.write(config_path, result_config) do with {:error, err} <- File.write(config_path, result_config) do
:ok -> shell_error(
:ok "\nERROR: Unable to write config file to #{config_path}. Make sure you have permissions on the destination and that the parent path exists.\n"
)
{:error, err} -> {:error, err}
shell_error(
"\nERROR: Unable to write config file to #{config_path}. Make sure you have permissions on the destination and that the parent path exists.\n"
)
{:error, err}
end end
end end
@spec write_psql(String.t(), String.t()) :: :ok | {:error, atom()}
defp write_psql(psql_path, result_psql) do defp write_psql(psql_path, result_psql) do
shell_info("Writing #{psql_path}.") shell_info("Writing #{psql_path}.")
case File.write(psql_path, result_psql) do with {:error, err} <- File.write(psql_path, result_psql) do
:ok -> shell_error(
:ok "\nERROR: Unable to write psql file to #{psql_path}. Make sure you have permissions on the destination and that the parent path exists.\n"
)
{:error, err} -> {:error, err}
shell_error(
"\nERROR: Unable to write psql file to #{psql_path}. Make sure you have permissions on the destination and that the parent path exists.\n"
)
{:error, err}
end end
end end
end end

View File

@ -34,22 +34,16 @@ defmodule Mix.Tasks.Mobilizon.Media.CleanOrphan do
start_mobilizon() start_mobilizon()
case CleanOrphanMedia.clean(dry_run: dry_run, grace_period: grace_period) do {:ok, medias} = CleanOrphanMedia.clean(dry_run: dry_run, grace_period: grace_period)
{:ok, medias} ->
if length(medias) > 0 do
if dry_run or verbose do
details(medias, dry_run, verbose)
end
result(dry_run, length(medias)) if length(medias) > 0 do
else if dry_run or verbose do
empty_result(dry_run) details(medias, dry_run, verbose)
end end
:ok result(dry_run, length(medias))
else
_err -> empty_result(dry_run)
shell_error("Error while cleaning orphan media files")
end end
end end
@ -70,7 +64,7 @@ defmodule Mix.Tasks.Mobilizon.Media.CleanOrphan do
end) end)
end end
@spec result(boolean(), boolean()) :: :ok @spec result(boolean(), non_neg_integer()) :: :ok
defp result(dry_run, nb_medias) do defp result(dry_run, nb_medias) do
if dry_run do if dry_run do
shell_info("#{nb_medias} files would have been deleted") shell_info("#{nb_medias} files would have been deleted")

View File

@ -13,7 +13,7 @@ defmodule Mix.Tasks.Mobilizon.Relay.Accept do
start_mobilizon() start_mobilizon()
case Relay.accept(target) do case Relay.accept(target) do
{:ok, _activity} -> {:ok, _activity, _follow} ->
# put this task to sleep to allow the genserver to push out the messages # put this task to sleep to allow the genserver to push out the messages
:timer.sleep(500) :timer.sleep(500)

View File

@ -35,23 +35,20 @@ defmodule Mix.Tasks.Mobilizon.Users.Clean do
start_mobilizon() start_mobilizon()
case CleanUnconfirmedUsers.clean(dry_run: dry_run, grace_period: grace_period) do {:ok, deleted_users} =
{:ok, deleted_users} -> CleanUnconfirmedUsers.clean(dry_run: dry_run, grace_period: grace_period)
if length(deleted_users) > 0 do
if dry_run or verbose do
details(deleted_users, dry_run, verbose)
end
result(dry_run, length(deleted_users)) if length(deleted_users) > 0 do
else if dry_run or verbose do
empty_result(dry_run) details(deleted_users, dry_run, verbose)
end end
:ok result(dry_run, length(deleted_users))
else
_err -> empty_result(dry_run)
shell_error("Error while cleaning unconfirmed users")
end end
:ok
end end
@spec details(list(Media.t()), boolean(), boolean()) :: :ok @spec details(list(Media.t()), boolean(), boolean()) :: :ok

View File

@ -48,20 +48,18 @@ defmodule Mobilizon.Admin do
@spec log_action(Actor.t(), String.t(), struct()) :: @spec log_action(Actor.t(), String.t(), struct()) ::
{:ok, ActionLog.t()} | {:error, Ecto.Changeset.t() | :user_not_moderator} {:ok, ActionLog.t()} | {:error, Ecto.Changeset.t() | :user_not_moderator}
def log_action(%Actor{user_id: user_id, id: actor_id}, action, target) do def log_action(%Actor{user_id: user_id, id: actor_id}, action, target) do
with %User{role: role} <- Users.get_user!(user_id), %User{role: role} = Users.get_user!(user_id)
{:role, true} <- {:role, role in [:administrator, :moderator]},
{:ok, %ActionLog{} = create_action_log} <- if role in [:administrator, :moderator] do
Admin.create_action_log(%{ Admin.create_action_log(%{
"actor_id" => actor_id, "actor_id" => actor_id,
"target_type" => to_string(target.__struct__), "target_type" => to_string(target.__struct__),
"target_id" => target.id, "target_id" => target.id,
"action" => action, "action" => action,
"changes" => stringify_struct(target) "changes" => stringify_struct(target)
}) do })
{:ok, create_action_log}
else else
{:role, false} -> {:error, :user_not_moderator}
{:error, :user_not_moderator}
end end
end end

View File

@ -115,13 +115,13 @@ defmodule Mobilizon.Discussions.Comment do
@doc """ @doc """
Checks whether an comment can be managed. Checks whether an comment can be managed.
""" """
@spec can_be_managed_by(t, integer | String.t()) :: boolean @spec can_be_managed_by?(t, integer | String.t()) :: boolean()
def can_be_managed_by(%__MODULE__{actor_id: creator_actor_id}, actor_id) def can_be_managed_by?(%__MODULE__{actor_id: creator_actor_id}, actor_id)
when creator_actor_id == actor_id do when creator_actor_id == actor_id do
{:comment_can_be_managed, true} creator_actor_id == actor_id
end end
def can_be_managed_by(_comment, _actor), do: {:comment_can_be_managed, false} def can_be_managed_by?(_comment, _actor), do: false
defp common_changeset(%__MODULE__{} = comment, attrs) do defp common_changeset(%__MODULE__{} = comment, attrs) do
comment comment

View File

@ -572,7 +572,7 @@ defmodule Mobilizon.Events do
@doc """ @doc """
Returns the list of tags. Returns the list of tags.
""" """
@spec list_tags(String.t() | nil, integer | nil, integer | nil) :: [Tag.t()] @spec list_tags(String.t() | nil, integer | nil, integer | nil) :: Page.t(Tag.t())
def list_tags(filter \\ nil, page \\ nil, limit \\ nil) do def list_tags(filter \\ nil, page \\ nil, limit \\ nil) do
Tag Tag
|> tag_filter(filter) |> tag_filter(filter)
@ -608,7 +608,7 @@ defmodule Mobilizon.Events do
Creates a relation between two tags. Creates a relation between two tags.
""" """
@spec create_tag_relation(map) :: {:ok, TagRelation.t()} | {:error, Changeset.t()} @spec create_tag_relation(map) :: {:ok, TagRelation.t()} | {:error, Changeset.t()}
def create_tag_relation(attrs \\ {}) do def create_tag_relation(attrs) do
%TagRelation{} %TagRelation{}
|> TagRelation.changeset(attrs) |> TagRelation.changeset(attrs)
|> Repo.insert( |> Repo.insert(

View File

@ -131,7 +131,7 @@ defmodule Mobilizon.Posts do
|> Repo.all() |> Repo.all()
end end
@spec tags_for_post_query(integer) :: Ecto.Query.t() @spec tags_for_post_query(String.t()) :: Ecto.Query.t()
defp tags_for_post_query(post_id) do defp tags_for_post_query(post_id) do
from( from(
t in Tag, t in Tag,

View File

@ -49,7 +49,8 @@ defmodule Mobilizon.Reports do
@doc """ @doc """
Returns the list of reports. Returns the list of reports.
""" """
@spec list_reports(integer | nil, integer | nil, atom, atom, ReportStatus) :: Page.t() @spec list_reports(integer | nil, integer | nil, atom, atom, ReportStatus.t()) ::
Page.t(Report.t())
def list_reports( def list_reports(
page \\ nil, page \\ nil,
limit \\ nil, limit \\ nil,

View File

@ -10,7 +10,7 @@ defmodule Mobilizon.Storage.Repo do
@doc """ @doc """
Dynamically loads the repository url from the DATABASE_URL environment variable. Dynamically loads the repository url from the DATABASE_URL environment variable.
""" """
@spec init(any(), any()) :: any() @spec init(any(), any()) :: {:ok, Keyword.t()}
def init(_, opts) do def init(_, opts) do
{:ok, opts} {:ok, opts}
end end

View File

@ -31,8 +31,8 @@ defmodule Mobilizon.Users.User do
feed_tokens: [FeedToken.t()], feed_tokens: [FeedToken.t()],
last_sign_in_at: DateTime.t(), last_sign_in_at: DateTime.t(),
last_sign_in_ip: String.t(), last_sign_in_ip: String.t(),
current_sign_in_ip: String.t(), current_sign_in_ip: String.t() | nil,
current_sign_in_at: DateTime.t(), current_sign_in_at: DateTime.t() | nil,
activity_settings: [ActivitySetting.t()], activity_settings: [ActivitySetting.t()],
settings: Setting.t(), settings: Setting.t(),
unconfirmed_email: String.t() | nil unconfirmed_email: String.t() | nil

View File

@ -6,8 +6,8 @@ defmodule Mobilizon.Service.Activity do
alias Mobilizon.Activities.Activity alias Mobilizon.Activities.Activity
alias Mobilizon.Service.Activity.{Comment, Discussion, Event, Group, Member, Post, Resource} alias Mobilizon.Service.Activity.{Comment, Discussion, Event, Group, Member, Post, Resource}
@callback insert_activity(entity :: struct(), options :: map()) :: @callback insert_activity(entity :: struct(), options :: Keyword.t()) ::
{:ok, Oban.Job.t()} | {:ok, any()} {:ok, Oban.Job.t()} | {:ok, any()} | {:error, Ecto.Changeset.t()}
@callback get_object(object_id :: String.t() | integer()) :: struct() @callback get_object(object_id :: String.t() | integer()) :: struct()

View File

@ -40,7 +40,7 @@ defmodule Mobilizon.Service.Activity.Member do
Actors.get_member(member_id) Actors.get_member(member_id)
end end
@spec get_author(Member.t(), Member.t() | nil) :: integer() @spec get_author(Member.t(), Keyword.t()) :: integer()
defp get_author(%Member{actor_id: actor_id}, options) do defp get_author(%Member{actor_id: actor_id}, options) do
moderator = Keyword.get(options, :moderator) moderator = Keyword.get(options, :moderator)

View File

@ -191,14 +191,10 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
]) ])
end end
@spec register_user(String.t()) :: User.t() | any() @spec register_user(String.t()) :: User.t() | {:error, Ecto.Changeset.t()}
defp register_user(email) do defp register_user(email) do
case Users.create_external(email, "ldap") do with {:ok, %User{} = user} <- Users.create_external(email, "ldap") do
{:ok, %User{} = user} -> user
user
error ->
error
end end
end end

View File

@ -19,7 +19,7 @@ defmodule Mobilizon.Service.CleanOrphanMedia do
* `grace_period` how old in hours can the media be before it's taken into account for deletion * `grace_period` how old in hours can the media be before it's taken into account for deletion
* `dry_run` just return the media that would have been deleted, don't actually delete it * `dry_run` just return the media that would have been deleted, don't actually delete it
""" """
@spec clean(Keyword.t()) :: {:ok, list(Media.t())} | {:error, String.t()} @spec clean(Keyword.t()) :: {:ok, list(Media.t())}
def clean(opts \\ []) do def clean(opts \\ []) do
medias = find_media(opts) medias = find_media(opts)

View File

@ -15,7 +15,7 @@ defmodule Mobilizon.Service.CleanUnconfirmedUsers do
Remove media that is not attached to an entity, such as media uploads that were never used in entities. Remove media that is not attached to an entity, such as media uploads that were never used in entities.
""" """
@spec clean(Keyword.t()) :: {:ok, list(Media.t())} | {:error, String.t()} @spec clean(Keyword.t()) :: {:ok, list(Media.t())}
def clean(opts \\ []) do def clean(opts \\ []) do
users_to_delete = find_unconfirmed_users_to_clean(opts) users_to_delete = find_unconfirmed_users_to_clean(opts)

View File

@ -21,49 +21,41 @@ defmodule Mobilizon.Service.Export.Feed do
@item_limit 500 @item_limit 500
def version, do: Config.instance_version() @spec version :: String.t()
defp version, do: Config.instance_version()
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, any()} @spec create_cache(String.t()) ::
{:commit, String.t()}
| {:ignore, :actor_not_found | :actor_not_public | :bad_token | :token_not_found}
def create_cache("actor_" <> name) do def create_cache("actor_" <> name) do
case fetch_actor_event_feed(name) do case fetch_actor_event_feed(name) do
{:ok, res} -> {:ok, res} ->
{:commit, res} {:commit, res}
err -> {:error, err} ->
{:ignore, err} {:ignore, err}
end end
end end
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, any()}
def create_cache("token_" <> token) do def create_cache("token_" <> token) do
case fetch_events_from_token(token) do case fetch_events_from_token(token) do
{:ok, res} -> {:ok, res} ->
{:commit, res} {:commit, res}
err -> {:error, err} ->
{:ignore, err} {:ignore, err}
end end
end end
def create_cache("instance") do def create_cache("instance") do
case fetch_instance_feed() do {:ok, res} = fetch_instance_feed()
{:ok, res} -> {:commit, res}
{:commit, res}
err ->
{:ignore, err}
end
end end
@spec fetch_instance_feed :: {:ok, String.t()} @spec fetch_instance_feed :: {:ok, String.t()}
defp fetch_instance_feed do defp fetch_instance_feed do
case Common.fetch_instance_public_content(@item_limit) do {:ok, events, posts} = Common.fetch_instance_public_content(@item_limit)
{:ok, events, posts} -> {:ok, build_instance_feed(events, posts)}
{:ok, build_instance_feed(events, posts)}
err ->
{:error, err}
end
end end
# Build an atom feed from the whole instance and its public events and posts # Build an atom feed from the whole instance and its public events and posts
@ -90,7 +82,8 @@ defmodule Mobilizon.Service.Export.Feed do
|> Atomex.generate_document() |> Atomex.generate_document()
end end
@spec fetch_actor_event_feed(String.t(), integer()) :: String.t() @spec fetch_actor_event_feed(String.t(), integer()) ::
{:ok, String.t()} | {:error, :actor_not_found | :actor_not_public}
defp fetch_actor_event_feed(name, limit \\ @item_limit) do defp fetch_actor_event_feed(name, limit \\ @item_limit) do
case Common.fetch_actor_event_feed(name, limit) do case Common.fetch_actor_event_feed(name, limit) do
{:ok, actor, events, posts} -> {:ok, actor, events, posts} ->
@ -199,7 +192,8 @@ defmodule Mobilizon.Service.Export.Feed do
end end
# Only events, not posts # Only events, not posts
@spec fetch_events_from_token(String.t(), integer()) :: {:ok, String.t()} | {:error, atom()} @spec fetch_events_from_token(String.t(), integer()) ::
{:ok, String.t()} | {:error, :bad_token | :token_not_found}
defp fetch_events_from_token(token, limit \\ @item_limit) do defp fetch_events_from_token(token, limit \\ @item_limit) do
case Common.fetch_events_from_token(token, limit) do case Common.fetch_events_from_token(token, limit) do
%{events: events, token: token, user: user, actor: actor, type: type} -> %{events: events, token: token, user: user, actor: actor, type: type} ->

View File

@ -93,11 +93,7 @@ defmodule Mobilizon.Service.Geospatial.Addok do
if is_nil(value), do: url, else: do_add_parameter(url, key, value) if is_nil(value), do: url, else: do_add_parameter(url, key, value)
end end
@spec do_add_parameter(String.t(), :coords | :type, %{lat: float, lon: float} | :administrative) :: @spec do_add_parameter(String.t(), :type, :administrative | atom()) :: String.t()
String.t()
defp do_add_parameter(url, :coords, coords),
do: "#{url}&lat=#{coords.lat}&lon=#{coords.lon}"
defp do_add_parameter(url, :type, :administrative), defp do_add_parameter(url, :type, :administrative),
do: "#{url}&type=municipality" do: "#{url}&type=municipality"

View File

@ -31,16 +31,18 @@ defmodule Mobilizon.Service.Geospatial.GoogleMaps do
@doc """ @doc """
Google Maps implementation for `c:Mobilizon.Service.Geospatial.Provider.geocode/3`. Google Maps implementation for `c:Mobilizon.Service.Geospatial.Provider.geocode/3`.
""" """
@spec geocode(String.t(), keyword()) :: list(Address.t()) | no_return @spec geocode(float(), float(), keyword()) :: list(Address.t())
def geocode(lon, lat, options \\ []) do def geocode(lon, lat, options \\ []) do
url = build_url(:geocode, %{lon: lon, lat: lat}, options) url = build_url(:geocode, %{lon: lon, lat: lat}, options)
Logger.debug("Asking Google Maps for reverse geocode with #{url}") Logger.debug("Asking Google Maps for reverse geocode with #{url}")
with {:ok, %{status: 200, body: body}} <- GeospatialClient.get(url), %Tesla.Env{status: 200, body: body} = GeospatialClient.get!(url)
%{"results" => results, "status" => "OK"} <- body do
Enum.map(results, fn entry -> process_data(entry, options) end) case body do
else %{"results" => results, "status" => "OK"} ->
Enum.map(results, &process_data(&1, options))
%{"status" => "REQUEST_DENIED", "error_message" => error_message} -> %{"status" => "REQUEST_DENIED", "error_message" => error_message} ->
raise ArgumentError, message: to_string(error_message) raise ArgumentError, message: to_string(error_message)
end end
@ -50,16 +52,18 @@ defmodule Mobilizon.Service.Geospatial.GoogleMaps do
@doc """ @doc """
Google Maps implementation for `c:Mobilizon.Service.Geospatial.Provider.search/2`. Google Maps implementation for `c:Mobilizon.Service.Geospatial.Provider.search/2`.
""" """
@spec search(String.t(), keyword()) :: list(Address.t()) | no_return @spec search(String.t(), keyword()) :: list(Address.t())
def search(q, options \\ []) do def search(q, options \\ []) do
url = build_url(:search, %{q: q}, options) url = build_url(:search, %{q: q}, options)
Logger.debug("Asking Google Maps for addresses with #{url}") Logger.debug("Asking Google Maps for addresses with #{url}")
with {:ok, %{status: 200, body: body}} <- GeospatialClient.get(url), %Tesla.Env{status: 200, body: body} = GeospatialClient.get!(url)
%{"results" => results, "status" => "OK"} <- body do
results |> Enum.map(fn entry -> process_data(entry, options) end) case body do
else %{"results" => results, "status" => "OK"} ->
results |> Enum.map(fn entry -> process_data(entry, options) end)
%{"status" => "REQUEST_DENIED", "error_message" => error_message} -> %{"status" => "REQUEST_DENIED", "error_message" => error_message} ->
raise ArgumentError, message: to_string(error_message) raise ArgumentError, message: to_string(error_message)
@ -68,7 +72,7 @@ defmodule Mobilizon.Service.Geospatial.GoogleMaps do
end end
end end
@spec build_url(atom(), map(), list()) :: String.t() | no_return @spec build_url(:search | :geocode, map(), list()) :: String.t() | no_return
defp build_url(method, args, options) do defp build_url(method, args, options) do
limit = Keyword.get(options, :limit, 10) limit = Keyword.get(options, :limit, 10)
lang = Keyword.get(options, :lang, "en") lang = Keyword.get(options, :lang, "en")
@ -97,6 +101,7 @@ defmodule Mobilizon.Service.Geospatial.GoogleMaps do
URI.encode(uri) URI.encode(uri)
end end
@spec process_data(map(), Keyword.t()) :: Address.t()
defp process_data( defp process_data(
%{ %{
"formatted_address" => description, "formatted_address" => description,
@ -148,16 +153,18 @@ defmodule Mobilizon.Service.Geospatial.GoogleMaps do
end end
end end
@spec do_fetch_place_details(String.t() | nil, Keyword.t()) :: String.t() | no_return @spec do_fetch_place_details(String.t() | nil, Keyword.t()) :: String.t() | nil
defp do_fetch_place_details(place_id, options) do defp do_fetch_place_details(place_id, options) do
url = build_url(:place_details, %{place_id: place_id}, options) url = build_url(:place_details, %{place_id: place_id}, options)
Logger.debug("Asking Google Maps for details with #{url}") Logger.debug("Asking Google Maps for details with #{url}")
with {:ok, %{status: 200, body: body}} <- GeospatialClient.get(url), %Tesla.Env{status: 200, body: body} = GeospatialClient.get!(url)
%{"result" => %{"name" => name}, "status" => "OK"} <- body do
name case body do
else %{"result" => %{"name" => name}, "status" => "OK"} ->
name
%{"status" => "REQUEST_DENIED", "error_message" => error_message} -> %{"status" => "REQUEST_DENIED", "error_message" => error_message} ->
raise ArgumentError, message: to_string(error_message) raise ArgumentError, message: to_string(error_message)

View File

@ -43,7 +43,7 @@ defmodule Mobilizon.Service.Geospatial.Provider do
%Address{} %Address{}
""" """
@callback geocode(longitude :: number, latitude :: number, options :: keyword) :: @callback geocode(longitude :: number, latitude :: number, options :: keyword) ::
[Address.t()] | {:error, atom()} [Address.t()]
@doc """ @doc """
Search for an address Search for an address
@ -63,23 +63,21 @@ defmodule Mobilizon.Service.Geospatial.Provider do
iex> search("10 rue Jangot") iex> search("10 rue Jangot")
%Address{} %Address{}
""" """
@callback search(address :: String.t(), options :: keyword) :: [Address.t()] | {:error, atom()} @callback search(address :: String.t(), options :: keyword) :: [Address.t()]
@doc """ @doc """
Returns a `Geo.Point` for given coordinates Returns a `Geo.Point` for given coordinates
""" """
@spec coordinates([number | String.t()], number) :: Geo.Point.t() | nil @spec coordinates([number | String.t()]) :: Geo.Point.t() | nil
def coordinates(coords, srid \\ 4326) def coordinates([x, y]) when is_number(x) and is_number(y) do
%Geo.Point{coordinates: {x, y}, srid: 4326}
def coordinates([x, y], srid) when is_number(x) and is_number(y) do
%Geo.Point{coordinates: {x, y}, srid: srid}
end end
def coordinates([x, y], srid) when is_binary(x) and is_binary(y) do def coordinates([x, y]) when is_binary(x) and is_binary(y) do
%Geo.Point{coordinates: {String.to_float(x), String.to_float(y)}, srid: srid} %Geo.Point{coordinates: {String.to_float(x), String.to_float(y)}, srid: 4326}
end end
def coordinates(_, _), do: nil def coordinates(_), do: nil
@spec endpoint(atom()) :: String.t() @spec endpoint(atom()) :: String.t()
def endpoint(provider) do def endpoint(provider) do

View File

@ -113,17 +113,6 @@ defmodule Mix.Tasks.Mobilizon.Media.CleanOrphanTest do
end end
end end
describe "returns an error" do
test "for some reason" do
with_mock CleanOrphanMedia,
clean: fn [dry_run: false, grace_period: 48] -> {:error, "Some error"} end do
CleanOrphan.run([])
assert_received {:mix_shell, :error, [output_received]}
assert output_received == "Error while cleaning orphan media files"
end
end
end
defp create_file do defp create_file do
File.cp!("test/fixtures/picture.png", "test/fixtures/picture_tmp.png") File.cp!("test/fixtures/picture.png", "test/fixtures/picture_tmp.png")

View File

@ -117,15 +117,4 @@ defmodule Mix.Tasks.Mobilizon.Media.CleanUnconfirmedUsersTest do
end end
end end
end end
describe "returns an error" do
test "for some reason" do
with_mock CleanUnconfirmedUsers,
clean: fn [dry_run: false, grace_period: 48] -> {:error, "Some error"} end do
Clean.run([])
assert_received {:mix_shell, :error, [output_received]}
assert output_received == "Error while cleaning unconfirmed users"
end
end
end
end end