giant massive dep upgrade and dialyxir-found error emporium (#371)
[akkoma] / lib / pleroma / object.ex
index 5102be1decda2ca3807bdcf9a260573c1a00f21a..844251a18fe795a30946b0f09635352e83cc7f83 100644 (file)
@@ -62,26 +62,30 @@ defmodule Pleroma.Object do
     |> cast(params, [:data])
     |> validate_required([:data])
     |> unique_constraint(:ap_id, name: :objects_unique_apid_index)
+    # Expecting `maybe_handle_hashtags_change/1` to run last:
     |> maybe_handle_hashtags_change(struct)
   end
 
+  # Note: not checking activity type (assuming non-legacy objects are associated with Create act.)
   defp maybe_handle_hashtags_change(changeset, struct) do
-    with data_hashtags_change = get_change(changeset, :data),
-         true <- hashtags_changed?(struct, data_hashtags_change),
+    with %Ecto.Changeset{valid?: true} <- changeset,
+         data_hashtags_change = get_change(changeset, :data),
+         {_, true} <- {:changed, hashtags_changed?(struct, data_hashtags_change)},
          {:ok, hashtag_records} <-
            data_hashtags_change
            |> object_data_hashtags()
            |> Hashtag.get_or_create_by_names() do
       put_assoc(changeset, :hashtags, hashtag_records)
     else
-      false ->
+      %{valid?: false} ->
         changeset
 
-      {:error, hashtag_changeset} ->
-        failed_hashtag = get_field(hashtag_changeset, :name)
+      {:changed, false} ->
+        changeset
 
+      {:error, _} ->
         validate_change(changeset, :data, fn _, _ ->
-          [data: "error referencing hashtag: #{failed_hashtag}"]
+          [data: "error referencing hashtags"]
         end)
     end
   end
@@ -141,7 +145,7 @@ defmodule Pleroma.Object do
     Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
   end
 
-  def normalize(_, options \\ [fetch: false])
+  def normalize(_, options \\ [fetch: false, id_only: false])
 
   # If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
   # Use this whenever possible, especially when walking graphs in an O(N) loop!
@@ -169,10 +173,15 @@ defmodule Pleroma.Object do
   def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
 
   def normalize(ap_id, options) when is_binary(ap_id) do
-    if Keyword.get(options, :fetch) do
-      Fetcher.fetch_object_from_id!(ap_id, options)
-    else
-      get_cached_by_ap_id(ap_id)
+    cond do
+      Keyword.get(options, :id_only) ->
+        ap_id
+
+      Keyword.get(options, :fetch) ->
+        Fetcher.fetch_object_from_id!(ap_id, options)
+
+      true ->
+        get_cached_by_ap_id(ap_id)
     end
   end
 
@@ -204,10 +213,6 @@ defmodule Pleroma.Object do
     end
   end
 
-  def context_mapping(context) do
-    Object.change(%Object{}, %{data: %{"id" => context}})
-  end
-
   def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
     %ObjectTombstone{
       id: id,
@@ -220,9 +225,13 @@ defmodule Pleroma.Object do
   def swap_object_with_tombstone(object) do
     tombstone = make_tombstone(object)
 
-    object
-    |> Object.change(%{data: tombstone})
-    |> Repo.update()
+    with {:ok, object} <-
+           object
+           |> Object.change(%{data: tombstone})
+           |> Repo.update() do
+      Hashtag.unlink(object)
+      {:ok, object}
+    end
   end
 
   def delete(%Object{data: %{"id" => id}} = object) do
@@ -231,7 +240,7 @@ defmodule Pleroma.Object do
          {:ok, _} <- invalid_object_cache(object) do
       cleanup_attachments(
         Config.get([:instance, :cleanup_attachments]),
-        %{"object" => object}
+        %{object: object}
       )
 
       {:ok, object, deleted_activity}
@@ -240,7 +249,7 @@ defmodule Pleroma.Object do
 
   @spec cleanup_attachments(boolean(), %{required(:object) => map()}) ::
           {:ok, Oban.Job.t() | nil}
-  def cleanup_attachments(true, %{"object" => _} = params) do
+  def cleanup_attachments(true, %{object: _} = params) do
     AttachmentsCleanupWorker.enqueue("cleanup_attachments", params)
   end
 
@@ -358,7 +367,7 @@ defmodule Pleroma.Object do
   end
 
   def local?(%Object{data: %{"id" => id}}) do
-    String.starts_with?(id, Pleroma.Web.base_url() <> "/")
+    String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
   end
 
   def replies(object, opts \\ []) do
@@ -388,24 +397,16 @@ defmodule Pleroma.Object do
   def tags(_), do: []
 
   def hashtags(%Object{} = object) do
-    cond do
-      Config.object_embedded_hashtags?() ->
-        embedded_hashtags(object)
-
-      object.id == "pleroma:fake_object_id" ->
-        []
-
-      true ->
-        hashtag_records = Repo.preload(object, :hashtags).hashtags
-        Enum.map(hashtag_records, & &1.name)
-    end
+    # Note: always using embedded hashtags regardless whether they are migrated to hashtags table
+    #   (embedded hashtags stay in sync anyways, and we avoid extra joins and preload hassle)
+    embedded_hashtags(object)
   end
 
-  defp embedded_hashtags(%Object{data: data}) do
+  def embedded_hashtags(%Object{data: data}) do
     object_data_hashtags(data)
   end
 
-  defp embedded_hashtags(_), do: []
+  def embedded_hashtags(_), do: []
 
   def object_data_hashtags(%{"tag" => tags}) when is_list(tags) do
     tags
@@ -420,6 +421,8 @@ defmodule Pleroma.Object do
       hashtag when is_bitstring(hashtag) -> String.downcase(hashtag)
     end)
     |> Enum.uniq()
+    # Note: "" elements (plain text) might occur in `data.tag` for incoming objects
+    |> Enum.filter(&(&1 not in [nil, ""]))
   end
 
   def object_data_hashtags(_), do: []