X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fobject.ex;h=cc6fc9c5dfeb25c3311778fc4878090069c8dedd;hb=ad5263c647aea65dbeb4c329825671895e0a8863;hp=dabb495364277fdd0299a5e185db8947f26fd8bc;hpb=06c4935c11fc58f5ede18706bc46dcc502e64052;p=akkoma diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index dabb49536..cc6fc9c5d 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -5,44 +5,27 @@ defmodule Pleroma.Object do use Ecto.Schema - alias Pleroma.Repo - alias Pleroma.Object - alias Pleroma.User alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Object.Fetcher alias Pleroma.ObjectTombstone + alias Pleroma.Repo + alias Pleroma.User import Ecto.Query import Ecto.Changeset + require Logger + schema "objects" do field(:data, :map) timestamps() end - def insert_or_get(cng) do - {_, data} = fetch_field(cng, :data) - id = data["id"] || data[:id] - key = "object:#{id}" - - fetcher = fn _ -> - with nil <- get_by_ap_id(id), - {:ok, object} <- Repo.insert(cng) do - {:commit, object} - else - %Object{} = object -> {:commit, object} - e -> {:ignore, e} - end - end - - with {state, object} when state in [:commit, :ok] <- Cachex.fetch(:object_cache, key, fetcher) do - {:ok, object} - end - end - def create(data) do Object.change(%Object{}, %{data: data}) - |> insert_or_get() + |> Repo.insert() end def change(struct, params \\ %{}) do @@ -58,9 +41,44 @@ defmodule Pleroma.Object do Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) end - def normalize(%{"id" => ap_id}), do: normalize(ap_id) - def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) - def normalize(_), do: nil + def normalize(_, fetch_remote \\ true) + # 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! + def normalize(%Object{} = object, _), do: object + def normalize(%Activity{object: %Object{} = object}, _), do: object + + # A hack for fake activities + def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _) do + %Object{id: "pleroma:fake_object_id", data: data} + end + + # Catch and log Object.normalize() calls where the Activity's child object is not + # preloaded. + def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote) do + Logger.debug( + "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" + ) + + Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + + normalize(ap_id, fetch_remote) + end + + def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote) do + Logger.debug( + "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" + ) + + Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + + normalize(ap_id, fetch_remote) + end + + # Old way, try fetching the object through cache. + def normalize(%{"id" => ap_id}, fetch_remote), do: normalize(ap_id, fetch_remote) + def normalize(ap_id, false) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) + def normalize(ap_id, true) when is_binary(ap_id), do: Fetcher.fetch_object_from_id!(ap_id) + def normalize(_, _), do: nil # Owned objects can only be mutated by their owner def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}), @@ -106,7 +124,14 @@ defmodule Pleroma.Object do def delete(%Object{data: %{"id" => id}} = object) do with {:ok, _obj} = swap_object_with_tombstone(object), - Repo.delete_all(Activity.by_object_ap_id(id)), + deleted_activity = Activity.delete_by_ap_id(id), + {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do + {:ok, object, deleted_activity} + end + end + + def prune(%Object{data: %{"id" => id}} = object) do + with {:ok, object} <- Repo.delete(object), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do {:ok, object} end @@ -124,4 +149,50 @@ defmodule Pleroma.Object do e -> e end end + + def increase_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def decrease_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end end