From db9564cbaee549a5446d1ff9cb8155eb92fa50c5 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 30 Sep 2019 16:52:50 -0400 Subject: [PATCH] Add object getting/fetching --- lib/clacks/activitypub.ex | 13 +++++++++ lib/clacks/object.ex | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/lib/clacks/activitypub.ex b/lib/clacks/activitypub.ex index a894513..bd83de6 100644 --- a/lib/clacks/activitypub.ex +++ b/lib/clacks/activitypub.ex @@ -57,6 +57,19 @@ defmodule Clacks.ActivityPub do } end + @spec synthesized_create(object :: map()) :: map() + def synthesized_create(object) do + %{ + "@context" => @context, + "type" => "Create", + "object" => object, + # Mastodon doesn't include 'actor' in notes + "actor" => object["actor"] || object["attributedTo"], + "to" => object["to"], + "cc" => object["cc"] + } + end + @spec object_id(id :: String.t()) :: String.t() def object_id(id) do url = Application.get_env(:clacks, ClacksWeb.Endpoint)[:url] diff --git a/lib/clacks/object.ex b/lib/clacks/object.ex index 73e5fc1..2a1faa9 100644 --- a/lib/clacks/object.ex +++ b/lib/clacks/object.ex @@ -1,6 +1,9 @@ defmodule Clacks.Object do + require Logger use Ecto.Schema import Ecto.Changeset + alias Clacks.Repo + import Ecto.Query @type t() :: %__MODULE__{} @@ -15,4 +18,61 @@ defmodule Clacks.Object do |> cast(attrs, [:data]) |> validate_required([:data]) end + + @spec get_by_ap_id( + ap_id :: String.t(), + force_refetch :: boolean(), + synthesize_create :: boolean() + ) :: t() | nil + def get_by_ap_id(ap_id, force_refetch \\ false, synthesize_create \\ true) + when is_binary(ap_id) do + if force_refetch do + fetch(ap_id, synthesize_create) + else + get_cached_by_ap_id(ap_id) || fetch(ap_id, synthesize_create) + end + end + + @spec get_cached_by_ap_id(ap_id :: String.t()) :: t() | nil + def get_cached_by_ap_id(ap_id) when is_binary(ap_id) do + Repo.one(from o in __MODULE__, where: fragment("?->>'id'", o.data) == ^ap_id) + end + + @spec fetch(ap_id :: String.t(), synthesize_create :: boolean()) :: t() | nil + def fetch(ap_id, synthesize_create \\ true) when is_binary(ap_id) do + case Clacks.ActivityPub.Fetcher.fetch_object(ap_id) do + nil -> + nil + + data -> + existing = get_cached_by_ap_id(data["id"]) + + changeset = + changeset(existing || %__MODULE__{}, %{ + data: data + }) + + case Repo.insert_or_update(changeset) do + {:ok, object} -> + if synthesize_create do + create = Clacks.ActivityPub.synthesized_create(data) + + changeset = + Clacks.Activity.changeset(%Clacks.Activity{}, %{ + data: create, + local: false, + actor: data["actor"] + }) + + {:ok, _create} = Repo.insert_or_update(changeset) + end + + object + + {:error, changeset} -> + Logger.error("Couldn't store remote object #{ap_id}: #{inspect(changeset)}") + nil + end + end + end end