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 max timeout: 10 min
|
||||||
gc_cleanup_max_timeout: :timer.minutes(10)
|
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,
|
config :vonbraun,
|
||||||
json_ld: %{
|
json_ld: %{}
|
||||||
"https://www.w3.org/ns/activitystreams" =>
|
|
||||||
File.read!(Path.join(Path.dirname(__DIR__), "priv/jsonld/activitystreams.json"))
|
|
||||||
}
|
|
||||||
|
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -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)}"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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