diff --git a/appview/lib/atvouch/identity.ex b/appview/lib/atvouch/identity.ex index e1f9101..8dea4a1 100644 --- a/appview/lib/atvouch/identity.ex +++ b/appview/lib/atvouch/identity.ex @@ -23,6 +23,18 @@ defmodule Atvouch.Identity do |> Atvouch.Repo.insert() end + def upsert(attrs) do + case one(attrs[:did] || attrs["did"]) do + nil -> + create(attrs) + + existing -> + existing + |> changeset(attrs) + |> Atvouch.Repo.update() + end + end + def one(did) do Atvouch.Repo.get(__MODULE__, did) end diff --git a/appview/lib/atvouch/tap_handler.ex b/appview/lib/atvouch/tap_handler.ex index 39ce43e..25659a1 100644 --- a/appview/lib/atvouch/tap_handler.ex +++ b/appview/lib/atvouch/tap_handler.ex @@ -2,39 +2,90 @@ defmodule Atvouch.TapHandler do @behaviour Atvouch.Tap.Handler require Logger - # TODO: implement vouch creation from tap events - # @impl true - # def handle_record(%{collection: "dev.atvouch.graph.vouch", action: :create} = event) do - # Logger.info("tap record: #{event.action} #{event.collection}/#{event.rkey} from #{event.did}") - # - # # Ensure the creator identity exists - # case Atvouch.Identity.one(event.did) do - # nil -> Atvouch.Identity.create(%{did: event.did}) - # identity -> {:ok, identity} - # end - # - # at_uri = "at://#{event.did}/#{event.collection}/#{event.rkey}" - # - # case Atvouch.Vouch.create(%{ - # at_uri: at_uri, - # creator_did: event.did, - # target_did: event.record["subject"], - # created_at: event.record["createdAt"] - # }) do - # {:ok, _vouch} -> :ok - # {:error, reason} -> {:error, reason} - # end - # end + @collection "dev.atvouch.graph.vouch" @impl true + def handle_record(%{collection: @collection, action: :create} = event) do + at_uri = "at://#{event.did}/#{@collection}/#{event.rkey}" + created_at = event.record["createdAt"] + target_did = event.record["subject"] + + # Ensure both identities exist + ensure_identity(event.did) + ensure_identity(target_did) + + case Atvouch.Vouch.create(%{ + at_uri: at_uri, + creator_did: event.did, + target_did: target_did, + original_created_at: created_at, + remote_created_at: created_at, + remote_updated_timestamp: false, + at_cid: event.cid, + live: event.live + }) do + {:ok, _vouch} -> :ok + {:error, reason} -> {:error, reason} + end + end + + def handle_record(%{collection: @collection, action: :update} = event) do + at_uri = "at://#{event.did}/#{@collection}/#{event.rkey}" + created_at = event.record["createdAt"] + + case Atvouch.Vouch.one(at_uri) do + nil -> + {:error, :not_found} + + vouch -> + updated_timestamp = created_at != vouch.original_created_at + + case Atvouch.Vouch.update(vouch, %{ + remote_created_at: created_at, + remote_updated_timestamp: updated_timestamp, + at_cid: event.cid, + live: event.live + }) do + {:ok, _vouch} -> :ok + {:error, reason} -> {:error, reason} + end + end + end + + def handle_record(%{collection: @collection, action: :delete} = event) do + at_uri = "at://#{event.did}/#{@collection}/#{event.rkey}" + + case Atvouch.Vouch.delete(at_uri) do + {:ok, _vouch} -> :ok + {:error, reason} -> {:error, reason} + end + end + def handle_record(event) do - Logger.info("tap record: #{event.action} #{event.collection}/#{event.rkey} from #{event.did}") - {:error, :skip} + Logger.debug( + "explicit skip for record event #{inspect(event)}, not handled by others. is tap misconfigured?" + ) + + {:error, :invalid_collection_or_action} end @impl true def handle_identity(event) do - Logger.info("tap identity: #{event.did} handle=#{event.handle} active=#{event.is_active}") - {:error, :skip} + case Atvouch.Identity.upsert(%{ + did: event.did, + handle: event.handle, + active: event.is_active, + status: event.status + }) do + {:ok, _identity} -> :ok + {:error, reason} -> {:error, reason} + end + end + + defp ensure_identity(did) do + case Atvouch.Identity.one(did) do + nil -> Atvouch.Identity.create(%{did: did}) + _existing -> :ok + end end end diff --git a/appview/lib/atvouch/vouch.ex b/appview/lib/atvouch/vouch.ex index 1bcbd8a..995b890 100644 --- a/appview/lib/atvouch/vouch.ex +++ b/appview/lib/atvouch/vouch.ex @@ -28,6 +28,19 @@ defmodule Atvouch.Vouch do |> Atvouch.Repo.insert() end + def update(vouch, attrs) do + vouch + |> changeset(attrs) + |> Atvouch.Repo.update() + end + + def delete(at_uri) do + case one(at_uri) do + nil -> {:error, :not_found} + vouch -> Atvouch.Repo.delete(vouch) + end + end + def one(at_uri) do Atvouch.Repo.get(__MODULE__, at_uri) end