79 lines
1.8 KiB
Elixir
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
|