transmogrifier: gracefully ignore duplicated object deletes
The object lookup is later repeated in the validator, but due to caching shouldn't incur any noticeable performance impact. It’s actually preferable to check here, since it avoids the otherwise occuring user lookup and overhead from starting and aborting a transaction
This commit is contained in:
parent
ac2327c8fc
commit
cd8e6a4235
2 changed files with 19 additions and 4 deletions
|
@ -215,6 +215,11 @@ def get_cached_by_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Intentionally accepts non-Object arguments!
|
||||||
|
@spec is_tombstone_object?(term()) :: boolean()
|
||||||
|
def is_tombstone_object?(%Object{data: %{"type" => "Tombstone"}}), do: true
|
||||||
|
def is_tombstone_object?(_), do: false
|
||||||
|
|
||||||
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
|
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
|
||||||
%ObjectTombstone{
|
%ObjectTombstone{
|
||||||
id: id,
|
id: id,
|
||||||
|
|
|
@ -556,19 +556,29 @@ defp handle_incoming_normalised(
|
||||||
%{"type" => "Delete"} = data,
|
%{"type" => "Delete"} = data,
|
||||||
_options
|
_options
|
||||||
) do
|
) do
|
||||||
with {:ok, activity, _} <-
|
oid_result = ObjectValidators.ObjectID.cast(data["object"])
|
||||||
Pipeline.common_pipeline(data, local: false) do
|
|
||||||
|
with {_, {:ok, object_id}} <- {:object_id, oid_result},
|
||||||
|
object <- Object.get_cached_by_ap_id(object_id),
|
||||||
|
{_, false} <- {:tombstone, Object.is_tombstone_object?(object) && !data["actor"]},
|
||||||
|
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
|
{:object_id, _} ->
|
||||||
|
{:error, {:validate, "Invalid object id: #{data["object"]}"}}
|
||||||
|
|
||||||
|
{:tombstone, true} ->
|
||||||
|
{:error, :ignore}
|
||||||
|
|
||||||
{:error, {:validate, {:error, %Ecto.Changeset{errors: errors}}}} = e ->
|
{:error, {:validate, {:error, %Ecto.Changeset{errors: errors}}}} = e ->
|
||||||
if errors[:object] == {"can't find object", []} do
|
if errors[:object] == {"can't find object", []} do
|
||||||
# Check if we have a create activity for this
|
# Check if we have a create activity for this
|
||||||
# (e.g. from a db prune without --prune-activities)
|
# (e.g. from a db prune without --prune-activities)
|
||||||
# We'd still like to process side effects so insert a tombstone and retry
|
# We'd still like to process side effects so insert a fake tombstone and retry
|
||||||
|
# (real tombstones from Object.delete do not have an actor field)
|
||||||
with {:ok, object_id} <- ObjectValidators.ObjectID.cast(data["object"]),
|
with {:ok, object_id} <- ObjectValidators.ObjectID.cast(data["object"]),
|
||||||
%Activity{data: %{"actor" => actor}} <-
|
%Activity{data: %{"actor" => actor}} <-
|
||||||
Activity.create_by_object_ap_id(object_id) |> Repo.one(),
|
Activity.create_by_object_ap_id(object_id) |> Repo.one(),
|
||||||
# We have one, insert a tombstone and retry
|
|
||||||
{:ok, tombstone_data, _} <- Builder.tombstone(actor, object_id),
|
{:ok, tombstone_data, _} <- Builder.tombstone(actor, object_id),
|
||||||
{:ok, _tombstone} <- Object.create(tombstone_data) do
|
{:ok, _tombstone} <- Object.create(tombstone_data) do
|
||||||
handle_incoming(data)
|
handle_incoming(data)
|
||||||
|
|
Loading…
Reference in a new issue