"""
end
# ---- data loading per panel ----
defp load_panel(socket, :networks, _params) do
assign(socket, :networks, Introspect.list_networks())
end
defp load_panel(socket, action, params)
when action in [:topology, :messages, :metrics, :coverage] do
case fetch_network(params["network"]) do
{:ok, network} ->
socket
|> assign(:network, network)
|> maybe_subscribe(action, network)
:error ->
assign(socket, :network, nil)
end
end
defp maybe_subscribe(socket, action, network) when action in [:topology, :metrics],
do: subscribe_metrics(socket, network)
defp maybe_subscribe(socket, :messages, network), do: subscribe_flow(socket, network)
defp maybe_subscribe(socket, _action, _network), do: socket
# Subscribe to the network's flow frames and prime the first paint.
defp subscribe_flow(socket, network) do
if connected?(socket) do
topic = Collector.flow_topic(network.id)
if socket.assigns[:flow_topic] != topic do
if old = socket.assigns[:flow_topic], do: Phoenix.PubSub.unsubscribe(@pubsub, old)
Phoenix.PubSub.subscribe(@pubsub, topic)
end
socket
|> assign(:flow_topic, topic)
|> assign(:flow, Collector.flow_snapshot(network.id))
else
socket
end
end
# Subscribe to the network's metric frames (idempotently) and prime the first
# paint from the collector snapshot. No-op until the socket is connected.
defp subscribe_metrics(socket, network) do
if connected?(socket) do
topic = Collector.topic(network.id)
if socket.assigns[:metrics_topic] != topic do
if old = socket.assigns[:metrics_topic], do: Phoenix.PubSub.unsubscribe(@pubsub, old)
Phoenix.PubSub.subscribe(@pubsub, topic)
end
socket
|> assign(:metrics_topic, topic)
|> put_frame(Collector.snapshot(network.id))
else
socket
end
end
defp put_frame(socket, frame) do
socket
|> assign(:frame, frame)
|> assign(:node_states, node_states(frame))
end
defp node_states(%{nodes: nodes}), do: Map.new(nodes, fn {id, v} -> {id, v.state} end)
defp node_states(_), do: %{}
defp blank(v) when v in [nil, ""], do: nil
defp blank(v), do: v
# Build the coverage report from trace events and stash it (plus a re-encoded
# .bloccs-trace for the gated export).
defp put_coverage(socket, events, source) do
network = socket.assigns.network
report = Coverage.report(network, Trace.reached(events))
assign(socket, :coverage, %{
report: report,
source: source,
json: encode_trace(events, network.id)
})
end
defp encode_trace(events, network_id) do
path =
Path.join(
System.tmp_dir!(),
"bloccs-#{network_id}-#{System.unique_integer([:positive])}.bloccs-trace"
)
case Trace.dump(events, network_id, path) do
:ok ->
json = File.read!(path)
_ = File.rm(path)
json
_ ->
nil
end
end
defp fetch_network(nil), do: :error
defp fetch_network(id) when is_binary(id) do
# A network that was started (so its atom exists) but has since stopped
# returns `{:error, :not_found}` — collapse it, and an unknown atom, to the
# one `:error` the caller renders as the not-found panel.
case Introspect.network(String.to_existing_atom(id)) do
{:ok, network} -> {:ok, network}
{:error, :not_found} -> :error
end
rescue
ArgumentError -> :error
end
# ---- rendering dispatch ----
defp panel_body(%{live_action: :networks} = assigns) do
~H"""
"""
end
defp panel_body(%{network: nil} = assigns) do
~H"""
Network not found.
It may have stopped. <.link navigate={Paths.networks(@base_path)} class="bloccs-link">
Back to networks.
"""
end
defp panel_body(%{live_action: :topology} = assigns) do
~H"""
"""
end
defp panel_body(%{live_action: :messages} = assigns) do
~H"""
"""
end
defp panel_body(%{live_action: :metrics} = assigns) do
~H"""
"""
end
defp panel_body(%{live_action: :coverage} = assigns) do
~H"""
"""
end
# ---- chrome ----
attr :active, :atom, required: true
attr :base_path, :string, required: true
attr :network, :any, default: nil
attr :features, :any, required: true
defp panel_nav(assigns) do
~H"""
"""
end
attr :active, :atom, required: true
attr :action, :atom, required: true
attr :href, :string, required: true
attr :label, :string, required: true
defp nav_link(assigns) do
~H"""
<.link
navigate={@href}
class={["bloccs-nav__link", @active == @action && "bloccs-nav__link--active"]}
>
{@label}
"""
end
defp page_title(:networks, _), do: "bloccs · networks"
defp page_title(action, %{"network" => net}), do: "bloccs · #{net} · #{action}"
defp page_title(action, _), do: "bloccs · #{action}"
# ---- resolver plumbing ----
defp call_resolver(resolver, fun, args, default) do
if function_exported?(resolver, fun, length(args)) do
apply(resolver, fun, args)
else
default
end
end
defp redirect_forbidden(socket, _reason) do
Phoenix.LiveView.redirect(socket, to: "/")
end
end