Various refactoring and typespec improvements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -38,52 +38,49 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|
||||
Converts an AP object data to our internal data structure.
|
||||
"""
|
||||
@impl Converter
|
||||
@spec as_to_model_data(map) :: map | {:error, any()}
|
||||
@spec as_to_model_data(map) :: map | {:error, atom}
|
||||
def as_to_model_data(object) do
|
||||
Logger.debug("We're converting raw ActivityStream data to a comment entity")
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
with {%Actor{id: actor_id, domain: actor_domain}, attributed_to} <-
|
||||
maybe_fetch_actor_and_attributed_to_id(object),
|
||||
{:tags, tags} <- {:tags, fetch_tags(Map.get(object, "tag", []))},
|
||||
{:mentions, mentions} <-
|
||||
{:mentions, fetch_mentions(Map.get(object, "tag", []))},
|
||||
discussion <-
|
||||
Discussions.get_discussion_by_url(Map.get(object, "context")) do
|
||||
Logger.debug("Inserting full comment")
|
||||
Logger.debug(inspect(object))
|
||||
tag_object = Map.get(object, "tag", [])
|
||||
|
||||
data = %{
|
||||
text: object["content"],
|
||||
url: object["id"],
|
||||
# Will be used in conversations, ignored in basic comments
|
||||
title: object["name"],
|
||||
context: object["context"],
|
||||
actor_id: actor_id,
|
||||
attributed_to_id: if(is_nil(attributed_to), do: nil, else: attributed_to.id),
|
||||
in_reply_to_comment_id: nil,
|
||||
event_id: nil,
|
||||
uuid: object["uuid"],
|
||||
discussion_id: if(is_nil(discussion), do: nil, else: discussion.id),
|
||||
tags: tags,
|
||||
mentions: mentions,
|
||||
local: is_nil(actor_domain),
|
||||
visibility: if(Visibility.is_public?(object), do: :public, else: :private),
|
||||
published_at: object["published"],
|
||||
is_announcement: Map.get(object, "isAnnouncement", false)
|
||||
}
|
||||
case maybe_fetch_actor_and_attributed_to_id(object) do
|
||||
{:ok, %Actor{id: actor_id, domain: actor_domain}, attributed_to} ->
|
||||
Logger.debug("Inserting full comment")
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
Logger.debug("Converted object before fetching parents")
|
||||
Logger.debug(inspect(data))
|
||||
data = %{
|
||||
text: object["content"],
|
||||
url: object["id"],
|
||||
# Will be used in conversations, ignored in basic comments
|
||||
title: object["name"],
|
||||
context: object["context"],
|
||||
actor_id: actor_id,
|
||||
attributed_to_id: if(is_nil(attributed_to), do: nil, else: attributed_to.id),
|
||||
in_reply_to_comment_id: nil,
|
||||
event_id: nil,
|
||||
uuid: object["uuid"],
|
||||
discussion_id: get_discussion_id(object),
|
||||
tags: fetch_tags(tag_object),
|
||||
mentions: fetch_mentions(tag_object),
|
||||
local: is_nil(actor_domain),
|
||||
visibility: if(Visibility.is_public?(object), do: :public, else: :private),
|
||||
published_at: object["published"],
|
||||
is_announcement: Map.get(object, "isAnnouncement", false)
|
||||
}
|
||||
|
||||
data = maybe_fetch_parent_object(object, data)
|
||||
Logger.debug("Converted object before fetching parents")
|
||||
Logger.debug(inspect(data))
|
||||
|
||||
Logger.debug("Converted object after fetching parents")
|
||||
Logger.debug(inspect(data))
|
||||
data
|
||||
else
|
||||
{:ok, %Actor{suspended: true}} ->
|
||||
:error
|
||||
data = maybe_fetch_parent_object(object, data)
|
||||
|
||||
Logger.debug("Converted object after fetching parents")
|
||||
Logger.debug(inspect(data))
|
||||
data
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -94,9 +91,20 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|
||||
"""
|
||||
@impl Converter
|
||||
@spec model_to_as(CommentModel.t()) :: map
|
||||
def model_to_as(%CommentModel{deleted_at: nil} = comment) do
|
||||
def model_to_as(
|
||||
%CommentModel{
|
||||
deleted_at: nil,
|
||||
attributed_to: attributed_to,
|
||||
actor: %Actor{url: comment_actor_url}
|
||||
} = comment
|
||||
) do
|
||||
to = determine_to(comment)
|
||||
|
||||
attributed_to =
|
||||
if is_nil(attributed_to),
|
||||
do: comment_actor_url,
|
||||
else: Map.get(attributed_to, :url, comment_actor_url)
|
||||
|
||||
object = %{
|
||||
"type" => "Note",
|
||||
"to" => to,
|
||||
@@ -104,9 +112,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|
||||
"content" => comment.text,
|
||||
"mediaType" => "text/html",
|
||||
"actor" => comment.actor.url,
|
||||
"attributedTo" =>
|
||||
if(is_nil(comment.attributed_to), do: nil, else: comment.attributed_to.url) ||
|
||||
comment.actor.url,
|
||||
"attributedTo" => attributed_to,
|
||||
"uuid" => comment.uuid,
|
||||
"id" => comment.url,
|
||||
"tag" => build_mentions(comment.mentions) ++ build_tags(comment.tags),
|
||||
@@ -132,7 +138,6 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|
||||
end
|
||||
|
||||
@impl Converter
|
||||
@spec model_to_as(CommentModel.t()) :: map
|
||||
def model_to_as(%CommentModel{} = comment) do
|
||||
Convertible.model_to_as(%TombstoneModel{
|
||||
uri: comment.url,
|
||||
@@ -203,4 +208,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
defp get_discussion_id(%{"context" => context}) do
|
||||
case Discussions.get_discussion_by_url(context) do
|
||||
%Discussion{id: discussion_id} -> discussion_id
|
||||
nil -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp get_discussion_id(_object), do: nil
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|
||||
@impl Converter
|
||||
@spec as_to_model_data(map) :: map() | {:error, any()} | :error
|
||||
def as_to_model_data(object) do
|
||||
with {%Actor{id: actor_id}, attributed_to} <-
|
||||
with {:ok, %Actor{id: actor_id}, attributed_to} <-
|
||||
maybe_fetch_actor_and_attributed_to_id(object),
|
||||
{:address, address_id} <-
|
||||
{:address, get_address(object["location"])},
|
||||
@@ -87,7 +87,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|
||||
language: object["inLanguage"]
|
||||
}
|
||||
else
|
||||
{:ok, %Actor{suspended: true}} ->
|
||||
{:error, _err} ->
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Media do
|
||||
internal one, and back.
|
||||
"""
|
||||
|
||||
alias Mobilizon.Federation.ActivityStream
|
||||
alias Mobilizon.Medias
|
||||
alias Mobilizon.Medias.Media, as: MediaModel
|
||||
|
||||
@@ -18,7 +19,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Media do
|
||||
@doc """
|
||||
Convert a media struct to an ActivityStream representation.
|
||||
"""
|
||||
@spec model_to_as(MediaModel.t()) :: map
|
||||
@spec model_to_as(MediaModel.t()) :: ActivityStream.t()
|
||||
def model_to_as(%MediaModel{file: file}) do
|
||||
%{
|
||||
"type" => "Document",
|
||||
@@ -31,29 +32,53 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Media do
|
||||
@doc """
|
||||
Save media data from raw data and return AS Link data.
|
||||
"""
|
||||
@spec find_or_create_media(map(), String.t() | integer()) ::
|
||||
{:ok, MediaModel.t()} | {:error, atom() | String.t() | Ecto.Changeset.t()}
|
||||
def find_or_create_media(%{"type" => "Link", "href" => url}, actor_id),
|
||||
do: find_or_create_media(url, actor_id)
|
||||
do:
|
||||
find_or_create_media(
|
||||
%{"type" => "Document", "url" => url, "name" => "External media"},
|
||||
actor_id
|
||||
)
|
||||
|
||||
def find_or_create_media(
|
||||
%{"type" => "Document", "url" => media_url, "name" => name},
|
||||
actor_id
|
||||
)
|
||||
when is_binary(media_url) do
|
||||
with {:ok, %{body: body}} <- Tesla.get(media_url, opts: @http_options),
|
||||
{:ok, %{url: url} = uploaded} <-
|
||||
Upload.store(%{body: body, name: name}),
|
||||
{:media_exists, nil} <- {:media_exists, Medias.get_media_by_url(url)} do
|
||||
Medias.create_media(%{
|
||||
file: Map.take(uploaded, [:url, :name, :content_type, :size]),
|
||||
metadata: Map.take(uploaded, [:width, :height, :blurhash]),
|
||||
actor_id: actor_id
|
||||
})
|
||||
else
|
||||
{:media_exists, %MediaModel{file: _file} = media} ->
|
||||
{:ok, media}
|
||||
case upload_media(media_url, name) do
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
|
||||
err ->
|
||||
err
|
||||
{:ok, %{url: url} = uploaded} ->
|
||||
case Medias.get_media_by_url(url) do
|
||||
%MediaModel{file: _file} = media ->
|
||||
{:ok, media}
|
||||
|
||||
nil ->
|
||||
Medias.create_media(%{
|
||||
file: Map.take(uploaded, [:url, :name, :content_type, :size]),
|
||||
metadata: Map.take(uploaded, [:width, :height, :blurhash]),
|
||||
actor_id: actor_id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec upload_media(String.t(), String.t()) :: {:ok, map()} | {:error, atom() | String.t()}
|
||||
defp upload_media(media_url, name) do
|
||||
case Tesla.get(media_url, opts: @http_options) do
|
||||
{:ok, %{body: body}} ->
|
||||
case Upload.store(%{body: body, name: name}) do
|
||||
{:ok, %{url: _url} = uploaded} ->
|
||||
{:ok, uploaded}
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,23 +51,31 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Todo do
|
||||
def as_to_model_data(
|
||||
%{"type" => "Todo", "actor" => actor_url, "todoList" => todo_list_url} = object
|
||||
) do
|
||||
with {:ok, %Actor{id: creator_id} = _creator} <-
|
||||
ActivityPubActor.get_or_fetch_actor_by_url(actor_url),
|
||||
{:todo_list, %TodoList{id: todo_list_id}} <-
|
||||
{:todo_list, Todos.get_todo_list_by_url(todo_list_url)} do
|
||||
%{
|
||||
title: object["name"],
|
||||
status: object["status"],
|
||||
url: object["id"],
|
||||
todo_list_id: todo_list_id,
|
||||
creator_id: creator_id,
|
||||
published_at: object["published"]
|
||||
}
|
||||
else
|
||||
{:todo_list, nil} ->
|
||||
with {:ok, %TodoList{}} <- ActivityPub.fetch_object_from_url(todo_list_url) do
|
||||
as_to_model_data(object)
|
||||
case ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do
|
||||
{:ok, %Actor{id: creator_id} = _creator} ->
|
||||
case Todos.get_todo_list_by_url(todo_list_url) do
|
||||
%TodoList{id: todo_list_id} ->
|
||||
%{
|
||||
title: object["name"],
|
||||
status: object["status"],
|
||||
url: object["id"],
|
||||
todo_list_id: todo_list_id,
|
||||
creator_id: creator_id,
|
||||
published_at: object["published"]
|
||||
}
|
||||
|
||||
nil ->
|
||||
case ActivityPub.fetch_object_from_url(todo_list_url) do
|
||||
{:ok, %TodoList{}} ->
|
||||
as_to_model_data(object)
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -111,7 +111,6 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
||||
acc ++ [%{actor_id: actor_id}]
|
||||
end
|
||||
|
||||
@spec create_mention(map(), list()) :: list()
|
||||
defp create_mention(mention, acc) when is_map(mention) do
|
||||
with true <- mention["type"] == "Mention",
|
||||
{:ok, %Actor{id: actor_id}} <-
|
||||
@@ -128,22 +127,34 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
||||
create_mention(mention, acc)
|
||||
end
|
||||
|
||||
@spec maybe_fetch_actor_and_attributed_to_id(map()) :: {Actor.t() | nil, Actor.t() | nil}
|
||||
@spec maybe_fetch_actor_and_attributed_to_id(map()) ::
|
||||
{:ok, Actor.t(), Actor.t() | nil} | {:error, atom()}
|
||||
def maybe_fetch_actor_and_attributed_to_id(%{
|
||||
"actor" => actor_url,
|
||||
"attributedTo" => attributed_to_url
|
||||
})
|
||||
when is_nil(attributed_to_url) do
|
||||
{fetch_actor(actor_url), nil}
|
||||
case fetch_actor(actor_url) do
|
||||
{:ok, %Actor{} = actor} ->
|
||||
{:ok, actor, nil}
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@spec maybe_fetch_actor_and_attributed_to_id(map()) :: {Actor.t() | nil, Actor.t() | nil}
|
||||
def maybe_fetch_actor_and_attributed_to_id(%{
|
||||
"actor" => actor_url,
|
||||
"attributedTo" => attributed_to_url
|
||||
})
|
||||
when is_nil(actor_url) do
|
||||
{fetch_actor(attributed_to_url), nil}
|
||||
case fetch_actor(attributed_to_url) do
|
||||
{:ok, %Actor{} = actor} ->
|
||||
{:ok, actor, nil}
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
# Only when both actor and attributedTo fields are both filled is when we can return both
|
||||
@@ -152,9 +163,12 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
||||
"attributedTo" => attributed_to_url
|
||||
})
|
||||
when actor_url != attributed_to_url do
|
||||
with actor <- fetch_actor(actor_url),
|
||||
attributed_to <- fetch_actor(attributed_to_url) do
|
||||
{actor, attributed_to}
|
||||
with {:ok, %Actor{} = actor} <- fetch_actor(actor_url),
|
||||
{:ok, %Actor{} = attributed_to} <- fetch_actor(attributed_to_url) do
|
||||
{:ok, actor, attributed_to}
|
||||
else
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -162,16 +176,25 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
||||
def maybe_fetch_actor_and_attributed_to_id(%{
|
||||
"attributedTo" => attributed_to_url
|
||||
}) do
|
||||
{fetch_actor(attributed_to_url), nil}
|
||||
case fetch_actor(attributed_to_url) do
|
||||
{:ok, %Actor{} = attributed_to} -> {:ok, attributed_to, nil}
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_fetch_actor_and_attributed_to_id(_), do: {nil, nil}
|
||||
def maybe_fetch_actor_and_attributed_to_id(_), do: {:error, :no_actor_found}
|
||||
|
||||
@spec fetch_actor(String.t()) :: Actor.t()
|
||||
@spec fetch_actor(String.t()) :: {:ok, Actor.t()} | {:error, atom()}
|
||||
defp fetch_actor(actor_url) do
|
||||
with {:ok, %Actor{suspended: false} = actor} <-
|
||||
ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do
|
||||
actor
|
||||
case ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do
|
||||
{:ok, %Actor{suspended: false} = actor} ->
|
||||
{:ok, actor}
|
||||
|
||||
{:ok, %Actor{suspended: true} = _actor} ->
|
||||
{:error, :actor_suspended}
|
||||
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -203,12 +226,17 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
||||
|> Map.new()
|
||||
|
||||
picture_id =
|
||||
with banner when is_map(banner) <- get_banner_picture(attachements),
|
||||
{:ok, %Media{id: picture_id}} <-
|
||||
MediaConverter.find_or_create_media(banner, actor_id) do
|
||||
picture_id
|
||||
else
|
||||
_err ->
|
||||
case get_banner_picture(attachements) do
|
||||
banner when is_map(banner) ->
|
||||
case MediaConverter.find_or_create_media(banner, actor_id) do
|
||||
{:error, _err} ->
|
||||
nil
|
||||
|
||||
{:ok, %Media{id: picture_id}} ->
|
||||
picture_id
|
||||
end
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user