defmodule Frajtano.Agent do alias Frajtano.BackingAgent use GenServer require Logger def start_link(args) do GenServer.start_link(__MODULE__, nil, [{:name, __MODULE__} | args]) end @impl true def init(_) do { :ok, %{}, } end def initial_backing_agents() do initial_backing_agents = Application.fetch_env!(:frajtano, :initial_backing_agents) |> Enum.map(&GenServer.call(__MODULE__, {:add_backing_agent, &1})) Logger.info("Started initial backing_agents: #{inspect initial_backing_agents}") end # select: list of specs, where specs are a tuple of match, guards, and outputs # match is {key, pid, value}, :"$1" is a match variable def backing_agent_paths() do Registry.select(Frajtano.BackingAgents, [{{:"$1", :_, :_}, [], [:"$1"]}]) end def backing_agent_pids() do Registry.select(Frajtano.BackingAgents, [{{:_, :"$1", :_}, [], [:"$1"]}]) end @impl true def handle_call({:identities}, _from, _state) do idents = Task.async_stream( backing_agent_pids(), &{&1, BackingAgent.identities(&1)}, ordered: false, on_timeout: :kill_task ) idents = for {:ok, {backing_agent, {:ok, idents}}} <- idents, do: {idents, backing_agent} { :reply, {:ok, idents |> Enum.flat_map(&elem(&1, 0)) |> Enum.uniq}, for({idents, backing_agent} <- idents, {key, _comment} <- idents, into: %{}, do: {key, backing_agent}) } end @impl true def handle_call({:sign, {key, _, _} = req}, _from, state) do {:reply, BackingAgent.sign(state[key], req), state} end @impl true def handle_call({:add_backing_agent, spec}, _from, state) do case BackingAgent.start(spec) do {:ok, _} -> {:reply, :ok, state} {:error, error} -> {:reply, {:error, error}, state} end end def identities() do GenServer.call(__MODULE__, {:identities}) end def sign(request) do # Signing can take some time, as a password may need to be entered or similar GenServer.call(__MODULE__, {:sign, request}, :infinity) end def assimilate(path) do GenServer.call(__MODULE__, {:add_backing_agent, {:socket, path}}) end def spawn_backing_agent(spec) do GenServer.call(__MODULE__, {:add_backing_agent, {:spawn, spec}}) end end