bunch of changes, json-ld document loader works now

This commit is contained in:
Moon Man 2024-09-05 17:56:16 +00:00
parent c99a4dc3ef
commit 9cf19af0a0
5 changed files with 118 additions and 11 deletions

View File

@ -14,10 +14,13 @@ config :vonbraun, Vonbraun.Cache,
# GC max timeout: 10 min # GC max timeout: 10 min
gc_cleanup_max_timeout: :timer.minutes(10) gc_cleanup_max_timeout: :timer.minutes(10)
config :vonbraun, #config :vonbraun,
json_ld: %{ # json_ld: %{
"https://www.w3.org/ns/activitystreams" => # "https://www.w3.org/ns/activitystreams" =>
File.read!(Path.join(Path.dirname(__DIR__), "priv/jsonld/activitystreams.json")) # File.read!(Path.join(Path.dirname(__DIR__), "priv/jsonld/activitystreams.json"))
} # }
config :vonbraun,
json_ld: %{}
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View File

@ -1,5 +1,6 @@
defmodule Vonbraun.ActivityPub.Object do defmodule Vonbraun.ActivityPub.Object do
@context "https://www.w3.org/ns/activitystreams" @context "https://www.w3.org/ns/activitystreams"
@public_to "https://www.w3.org/ns/activitystreams#Public"
@spec my_id() :: String.t() @spec my_id() :: String.t()
def my_id() do def my_id() do
@ -13,6 +14,24 @@ defmodule Vonbraun.ActivityPub.Object do
"#{my_id()}#main-key" "#{my_id()}#main-key"
end end
def add_context(object = %{"@context" => context})
when is_list(context) or is_binary(context) do
cond do
is_list(context) && @context not in context ->
Map.put(object, "@context", [@context | context])
is_binary(context) && @context != context ->
Map.put(object, "@context", [@context, context])
true ->
object
end
end
def add_context(object = %{}) do
Map.put(object, "@context", @context)
end
def activity(type, id, object, options \\ []) def activity(type, id, object, options \\ [])
when is_binary(type) and is_binary(id) and (is_binary(object) or is_map(object)) do when is_binary(type) and is_binary(id) and (is_binary(object) or is_map(object)) do
to = to =
@ -51,6 +70,37 @@ defmodule Vonbraun.ActivityPub.Object do
} }
end end
def copy_recipients(target = %{}, source = %{}) do
["to", "cc", "bcc", "bto"]
|> Enum.reduce(target, fn key, target ->
recipients = Map.get(source, key, [])
Map.put(target, key, recipients)
end)
end
def listify(string) when is_binary(string) do
[string]
end
def listify(list) when is_list(list) do
list
end
def mark_public(object = %{}) do
to = Map.get(object, "to", [])
cc = Map.get(object, "cc", [])
to_public? = to == @public_to or (is_list(to) && @public_to in to)
cc_public? = cc == @public_to or (is_list(cc) && @public_to in cc)
if to_public? || cc_public? do
object
else
to = [@public_to | listify(to)]
Map.put(object, "to", to)
end
end
@spec create_actor_activity_id(String.t(), String.t()) :: String.t() @spec create_actor_activity_id(String.t(), String.t()) :: String.t()
def create_actor_activity_id(actor_id, verb) when is_binary(actor_id) and is_binary(verb) do def create_actor_activity_id(actor_id, verb) when is_binary(actor_id) and is_binary(verb) do
"https://#{Application.fetch_env!(:vonbraun, :domain)}/id/#{URI.encode(verb)}:#{URI.encode(actor_id)}" "https://#{Application.fetch_env!(:vonbraun, :domain)}/id/#{URI.encode(verb)}:#{URI.encode(actor_id)}"

View File

@ -3,6 +3,7 @@ defmodule Vonbraun.Control do
alias Vonbraun.ActivityPub.Object alias Vonbraun.ActivityPub.Object
alias Vonbraun.ActivityPubReq alias Vonbraun.ActivityPubReq
alias Vonbraun.Ecto.Schema.Actor alias Vonbraun.Ecto.Schema.Actor
alias Vonbraun.CryptoID
@spec follow(String.t()) :: @spec follow(String.t()) ::
:ok :ok
@ -104,6 +105,38 @@ defmodule Vonbraun.Control do
end end
def post_note(content, public?, to \\ []) def post_note(content, public?, to \\ [])
when is_binary(content) and is_list(to) and is_boolean(public?) do when is_binary(content) and (is_list(to) or is_binary(to)) and is_boolean(public?) do
to = Object.listify(to)
{activity_id, now_ms} = CryptoID.now!()
object_id = CryptoID.encrypt_id(now_ms)
object =
%{
"@id" => object_id,
"@type" => "Note",
"to" => to,
"content" => content
}
|> Object.add_context()
object =
if public? do
Object.mark_public(object)
else
object
end
activity = Object.activity("Create", activity_id, object, to: to)
Logger.debug("Here is the raw activity:\n#{Jason.encode!(activity)}")
expanded_activity =
JSON.LD.expand(activity,
compact_arrays: false,
document_loader: Vonbraun.JSONLD.DocumentLoaderAgent
)
Logger.debug(("Here is the expanded activity:\n#{Jason.encode!(expanded_activity)}"))
activity
end end
end end

View File

@ -2,6 +2,7 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
alias JSON.LD.DocumentLoader.RemoteDocument alias JSON.LD.DocumentLoader.RemoteDocument
alias Req.Response alias Req.Response
use Agent use Agent
require Logger
@behaviour JSON.LD.DocumentLoader @behaviour JSON.LD.DocumentLoader
@ -10,7 +11,7 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
end end
defp preload() do defp preload() do
Application.get_env(:vonbraun, :jsonld) Application.get_env(:vonbraun, :json_ld)
|> Enum.reduce(Map.new(), fn {url, data}, map -> |> Enum.reduce(Map.new(), fn {url, data}, map ->
Map.put(map, url, {:ok, Jason.decode!(data)}) Map.put(map, url, {:ok, Jason.decode!(data)})
end) end)
@ -37,11 +38,11 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
{:ok, {:ok,
%Response{ %Response{
:status => status, :status => status,
:body => %{} = body, :body => body,
:headers => %{"content-type" => ["application/ld+json"]} :headers => %{"content-type" => ["application/ld+json"]}
}} }}
when status >= 200 and status <= 299 -> when status >= 200 and status <= 299 ->
{:ok, body} Jason.decode(body)
{:ok, %Response{:headers => %{"link" => [content]}}} when is_binary(content) -> {:ok, %Response{:headers => %{"link" => [content]}}} when is_binary(content) ->
with {link_url, props} <- parse_link_header(content), with {link_url, props} <- parse_link_header(content),
@ -72,18 +73,24 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
@spec load(String.t(), any()) :: {:ok, RemoteDocument.t()} | {:error, any()} @spec load(String.t(), any()) :: {:ok, RemoteDocument.t()} | {:error, any()}
def load(url, _options) when is_binary(url) do def load(url, _options) when is_binary(url) do
Logger.debug("Attempting to load JSON-LD context: #{url}")
with {:cache, nil} <- {:cache, Agent.get(__MODULE__, fn state -> Map.get(state, url) end)}, with {:cache, nil} <- {:cache, Agent.get(__MODULE__, fn state -> Map.get(state, url) end)},
:ok <- Logger.debug("No cached JSON-LD context, so querying"),
{:get, {:ok, body}} <- {:get, {:ok, body}} <-
{:get, http_get(url)}, {:get, http_get(url)},
{:data, data} <- {:data, %RemoteDocument{document: body, document_url: url}}, {:data, data} <- {:data, %RemoteDocument{document: body, document_url: url}},
{:update, :ok} <- {:update, :ok} <-
{:update, Agent.update(__MODULE__, fn state -> Map.put(state, url, data) end)} do {:update, Agent.update(__MODULE__, fn state -> Map.put(state, url, {:ok, data}) end)} do
{:ok, body} Logger.debug("Got remote one, so returning it.")
{:ok, data}
else else
{:cache, response} -> {:cache, response} ->
Logger.debug("Got cached version: #{inspect(response)}")
response response
{:get, error = {:error, _}} -> {:get, error = {:error, _}} ->
Logger.error("There was an error: #{inspect(error)}")
Agent.update(__MODULE__, fn state -> Map.put(state, url, error) end) Agent.update(__MODULE__, fn state -> Map.put(state, url, error) end)
error error
end end

View File

@ -0,0 +1,14 @@
{
"ledger": "vonbraun-test",
"insert": {
"@context": "https://www.w3.org/ns/activitystreams",
"@type": "Create",
"actor": "https://vonbraun.example.net/users/moonman",
"object": {
"@id": "https://vonbraun.example.net/id/1",
"@type": "Note",
"content": "This is a simple note."
},
"published": "2024-01-25T12:34:56Z"
}
}