Files
bDS2/lib/bds/mcp/proposal_store.ex

79 lines
1.8 KiB
Elixir

defmodule BDS.MCP.ProposalStore do
@moduledoc false
use Agent
alias BDS.Persistence
@default_ttl_ms 30 * 60 * 1000
def ensure_started do
case Process.whereis(__MODULE__) do
nil ->
case Agent.start_link(fn -> %{} end, name: __MODULE__) do
{:ok, _pid} -> :ok
{:error, {:already_started, _pid}} -> :ok
{:error, reason} -> {:error, reason}
end
_pid ->
:ok
end
end
def create(kind, data, opts \\ []) when is_binary(kind) and is_map(data) do
:ok = ensure_started()
cleanup_expired(opts)
proposal = %{
id: Ecto.UUID.generate(),
kind: kind,
data: data,
created_at: Persistence.now_ms(),
expires_at: Persistence.now_ms() + Keyword.get(opts, :ttl_ms, @default_ttl_ms)
}
Agent.update(__MODULE__, &Map.put(&1, proposal.id, proposal))
proposal
end
def get(id) when is_binary(id) do
:ok = ensure_started()
cleanup_expired([])
Agent.get(__MODULE__, &Map.get(&1, id))
end
def remove(id) when is_binary(id) do
:ok = ensure_started()
Agent.update(__MODULE__, &Map.delete(&1, id))
:ok
end
def list do
:ok = ensure_started()
cleanup_expired([])
Agent.get(__MODULE__, fn proposals ->
proposals
|> Map.values()
|> Enum.sort_by(& &1.created_at)
end)
end
def cleanup_expired(opts) do
:ok = ensure_started()
now = Persistence.now_ms()
on_expire = Keyword.get(opts, :on_expire)
Agent.get_and_update(__MODULE__, fn proposals ->
{expired, active} = Enum.split_with(proposals, fn {_id, proposal} -> proposal.expires_at <= now end)
Enum.each(expired, fn {_id, proposal} ->
if is_function(on_expire, 1), do: on_expire.(proposal)
end)
{Enum.map(expired, &elem(&1, 1)), Map.new(active)}
end)
end
end