bunch of changes, json-ld document loader works now
This commit is contained in:
parent
c99a4dc3ef
commit
9cf19af0a0
|
@ -14,10 +14,13 @@ config :vonbraun, Vonbraun.Cache,
|
|||
# GC max timeout: 10 min
|
||||
gc_cleanup_max_timeout: :timer.minutes(10)
|
||||
|
||||
config :vonbraun,
|
||||
json_ld: %{
|
||||
"https://www.w3.org/ns/activitystreams" =>
|
||||
File.read!(Path.join(Path.dirname(__DIR__), "priv/jsonld/activitystreams.json"))
|
||||
}
|
||||
#config :vonbraun,
|
||||
# json_ld: %{
|
||||
# "https://www.w3.org/ns/activitystreams" =>
|
||||
# File.read!(Path.join(Path.dirname(__DIR__), "priv/jsonld/activitystreams.json"))
|
||||
# }
|
||||
|
||||
config :vonbraun,
|
||||
json_ld: %{}
|
||||
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
defmodule Vonbraun.ActivityPub.Object do
|
||||
@context "https://www.w3.org/ns/activitystreams"
|
||||
@public_to "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
@spec my_id() :: String.t()
|
||||
def my_id() do
|
||||
|
@ -13,6 +14,24 @@ defmodule Vonbraun.ActivityPub.Object do
|
|||
"#{my_id()}#main-key"
|
||||
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 \\ [])
|
||||
when is_binary(type) and is_binary(id) and (is_binary(object) or is_map(object)) do
|
||||
to =
|
||||
|
@ -51,6 +70,37 @@ defmodule Vonbraun.ActivityPub.Object do
|
|||
}
|
||||
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()
|
||||
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)}"
|
||||
|
|
|
@ -3,6 +3,7 @@ defmodule Vonbraun.Control do
|
|||
alias Vonbraun.ActivityPub.Object
|
||||
alias Vonbraun.ActivityPubReq
|
||||
alias Vonbraun.Ecto.Schema.Actor
|
||||
alias Vonbraun.CryptoID
|
||||
|
||||
@spec follow(String.t()) ::
|
||||
:ok
|
||||
|
@ -104,6 +105,38 @@ defmodule Vonbraun.Control do
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -2,6 +2,7 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
|
|||
alias JSON.LD.DocumentLoader.RemoteDocument
|
||||
alias Req.Response
|
||||
use Agent
|
||||
require Logger
|
||||
|
||||
@behaviour JSON.LD.DocumentLoader
|
||||
|
||||
|
@ -10,7 +11,7 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
|
|||
end
|
||||
|
||||
defp preload() do
|
||||
Application.get_env(:vonbraun, :jsonld)
|
||||
Application.get_env(:vonbraun, :json_ld)
|
||||
|> Enum.reduce(Map.new(), fn {url, data}, map ->
|
||||
Map.put(map, url, {:ok, Jason.decode!(data)})
|
||||
end)
|
||||
|
@ -37,11 +38,11 @@ defmodule Vonbraun.JSONLD.DocumentLoaderAgent do
|
|||
{:ok,
|
||||
%Response{
|
||||
:status => status,
|
||||
:body => %{} = body,
|
||||
:body => body,
|
||||
:headers => %{"content-type" => ["application/ld+json"]}
|
||||
}}
|
||||
when status >= 200 and status <= 299 ->
|
||||
{:ok, body}
|
||||
Jason.decode(body)
|
||||
|
||||
{:ok, %Response{:headers => %{"link" => [content]}}} when is_binary(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()}
|
||||
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)},
|
||||
:ok <- Logger.debug("No cached JSON-LD context, so querying"),
|
||||
{:get, {:ok, body}} <-
|
||||
{:get, http_get(url)},
|
||||
{:data, data} <- {:data, %RemoteDocument{document: body, document_url: url}},
|
||||
{:update, :ok} <-
|
||||
{:update, Agent.update(__MODULE__, fn state -> Map.put(state, url, data) end)} do
|
||||
{:ok, body}
|
||||
{:update, Agent.update(__MODULE__, fn state -> Map.put(state, url, {:ok, data}) end)} do
|
||||
Logger.debug("Got remote one, so returning it.")
|
||||
{:ok, data}
|
||||
else
|
||||
{:cache, response} ->
|
||||
Logger.debug("Got cached version: #{inspect(response)}")
|
||||
response
|
||||
|
||||
{:get, error = {:error, _}} ->
|
||||
Logger.error("There was an error: #{inspect(error)}")
|
||||
Agent.update(__MODULE__, fn state -> Map.put(state, url, error) end)
|
||||
error
|
||||
end
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue