fix: guard pubsub APIs when not running
This commit is contained in:
parent
1e98a71431
commit
4d45c8351a
4 changed files with 120 additions and 10 deletions
|
|
@ -18,42 +18,64 @@ defmodule Aprsme.SpatialPubSub do
|
||||||
Register a client with their current viewport bounds.
|
Register a client with their current viewport bounds.
|
||||||
"""
|
"""
|
||||||
def register_viewport(client_id, bounds) do
|
def register_viewport(client_id, bounds) do
|
||||||
GenServer.call(__MODULE__, {:register_viewport, client_id, bounds})
|
with_server({:error, :not_running}, fn ->
|
||||||
|
GenServer.call(__MODULE__, {:register_viewport, client_id, bounds})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Update a client's viewport bounds.
|
Update a client's viewport bounds.
|
||||||
"""
|
"""
|
||||||
def update_viewport(client_id, bounds) do
|
def update_viewport(client_id, bounds) do
|
||||||
GenServer.call(__MODULE__, {:update_viewport, client_id, bounds})
|
with_server({:error, :not_running}, fn ->
|
||||||
|
GenServer.call(__MODULE__, {:update_viewport, client_id, bounds})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Unregister a client.
|
Unregister a client.
|
||||||
"""
|
"""
|
||||||
def unregister_client(client_id) do
|
def unregister_client(client_id) do
|
||||||
GenServer.cast(__MODULE__, {:unregister_client, client_id})
|
with_server(:ok, fn ->
|
||||||
|
GenServer.cast(__MODULE__, {:unregister_client, client_id})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Broadcast a packet to all clients whose viewports contain the packet's location.
|
Broadcast a packet to all clients whose viewports contain the packet's location.
|
||||||
"""
|
"""
|
||||||
def broadcast_packet(packet) do
|
def broadcast_packet(packet) do
|
||||||
GenServer.cast(__MODULE__, {:broadcast_packet, packet})
|
with_server(:ok, fn ->
|
||||||
|
GenServer.cast(__MODULE__, {:broadcast_packet, packet})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get statistics about spatial filtering.
|
Get statistics about spatial filtering.
|
||||||
"""
|
"""
|
||||||
def get_stats do
|
def get_stats do
|
||||||
GenServer.call(__MODULE__, :get_stats)
|
with_server(
|
||||||
|
%{
|
||||||
|
total_broadcasts: 0,
|
||||||
|
filtered_broadcasts: 0,
|
||||||
|
total_packets: 0,
|
||||||
|
clients_count: 0,
|
||||||
|
grid_cells: 0,
|
||||||
|
avg_clients_per_cell: 0.0
|
||||||
|
},
|
||||||
|
fn ->
|
||||||
|
GenServer.call(__MODULE__, :get_stats)
|
||||||
|
end
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Start telemetry reporting for LiveDashboard integration.
|
Start telemetry reporting for LiveDashboard integration.
|
||||||
"""
|
"""
|
||||||
def start_telemetry_reporting do
|
def start_telemetry_reporting do
|
||||||
GenServer.cast(__MODULE__, :start_telemetry_reporting)
|
with_server(:ok, fn ->
|
||||||
|
GenServer.cast(__MODULE__, :start_telemetry_reporting)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Server callbacks
|
# Server callbacks
|
||||||
|
|
@ -439,4 +461,12 @@ defmodule Aprsme.SpatialPubSub do
|
||||||
%{}
|
%{}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp with_server(default, fun) do
|
||||||
|
if Process.whereis(__MODULE__) do
|
||||||
|
fun.()
|
||||||
|
else
|
||||||
|
default
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -32,28 +32,36 @@ defmodule Aprsme.StreamingPacketsPubSub do
|
||||||
- :ok
|
- :ok
|
||||||
"""
|
"""
|
||||||
def subscribe_to_bounds(pid, bounds) do
|
def subscribe_to_bounds(pid, bounds) do
|
||||||
GenServer.call(__MODULE__, {:subscribe, pid, bounds})
|
with_server({:error, :not_running}, fn ->
|
||||||
|
GenServer.call(__MODULE__, {:subscribe, pid, bounds})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Unsubscribe from packet notifications.
|
Unsubscribe from packet notifications.
|
||||||
"""
|
"""
|
||||||
def unsubscribe(pid) do
|
def unsubscribe(pid) do
|
||||||
GenServer.call(__MODULE__, {:unsubscribe, pid})
|
with_server(:ok, fn ->
|
||||||
|
GenServer.call(__MODULE__, {:unsubscribe, pid})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Broadcast a packet to all subscribers whose bounds contain the packet's location.
|
Broadcast a packet to all subscribers whose bounds contain the packet's location.
|
||||||
"""
|
"""
|
||||||
def broadcast_packet(packet) do
|
def broadcast_packet(packet) do
|
||||||
GenServer.cast(__MODULE__, {:broadcast, packet})
|
with_server(:ok, fn ->
|
||||||
|
GenServer.cast(__MODULE__, {:broadcast, packet})
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
List all active subscribers and their bounds.
|
List all active subscribers and their bounds.
|
||||||
"""
|
"""
|
||||||
def list_subscribers do
|
def list_subscribers do
|
||||||
GenServer.call(__MODULE__, :list_subscribers)
|
with_server([], fn ->
|
||||||
|
GenServer.call(__MODULE__, :list_subscribers)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Server Callbacks
|
# Server Callbacks
|
||||||
|
|
@ -200,4 +208,12 @@ defmodule Aprsme.StreamingPacketsPubSub do
|
||||||
defp test_env? do
|
defp test_env? do
|
||||||
Application.get_env(:aprsme, :env) == :test
|
Application.get_env(:aprsme, :env) == :test
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp with_server(default, fun) do
|
||||||
|
if Process.whereis(__MODULE__) do
|
||||||
|
fun.()
|
||||||
|
else
|
||||||
|
default
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -97,4 +97,41 @@ defmodule Aprsme.SpatialPubSubTest do
|
||||||
SpatialPubSub.unregister_client(client_id)
|
SpatialPubSub.unregister_client(client_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "when the server is not running" do
|
||||||
|
setup do
|
||||||
|
pid = Process.whereis(SpatialPubSub)
|
||||||
|
|
||||||
|
if pid do
|
||||||
|
GenServer.stop(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
if !Process.whereis(SpatialPubSub) do
|
||||||
|
start_supervised!({SpatialPubSub, []})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "public APIs return safe defaults instead of crashing" do
|
||||||
|
bounds = %{north: 41.0, south: 40.0, east: -73.0, west: -74.0}
|
||||||
|
|
||||||
|
assert {:error, :not_running} = SpatialPubSub.register_viewport("missing", bounds)
|
||||||
|
assert {:error, :not_running} = SpatialPubSub.update_viewport("missing", bounds)
|
||||||
|
assert :ok = SpatialPubSub.unregister_client("missing")
|
||||||
|
assert :ok = SpatialPubSub.broadcast_packet(%{lat: 40.5, lon: -73.5})
|
||||||
|
assert :ok = SpatialPubSub.start_telemetry_reporting()
|
||||||
|
|
||||||
|
assert SpatialPubSub.get_stats() == %{
|
||||||
|
total_broadcasts: 0,
|
||||||
|
filtered_broadcasts: 0,
|
||||||
|
total_packets: 0,
|
||||||
|
clients_count: 0,
|
||||||
|
grid_cells: 0,
|
||||||
|
avg_clients_per_cell: 0.0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,33 @@ defmodule Aprsme.StreamingPacketsPubSubTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "when the server is not running" do
|
||||||
|
setup do
|
||||||
|
pid = Process.whereis(StreamingPacketsPubSub)
|
||||||
|
|
||||||
|
if pid do
|
||||||
|
GenServer.stop(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
if !Process.whereis(StreamingPacketsPubSub) do
|
||||||
|
start_supervised!({StreamingPacketsPubSub, []})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns safe defaults instead of crashing" do
|
||||||
|
bounds = %{north: 40.0, south: 30.0, east: -70.0, west: -80.0}
|
||||||
|
|
||||||
|
assert {:error, :not_running} = StreamingPacketsPubSub.subscribe_to_bounds(self(), bounds)
|
||||||
|
assert :ok = StreamingPacketsPubSub.unsubscribe(self())
|
||||||
|
assert :ok = StreamingPacketsPubSub.broadcast_packet(%{latitude: 35.0, longitude: -75.0})
|
||||||
|
assert [] == StreamingPacketsPubSub.list_subscribers()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "performance" do
|
describe "performance" do
|
||||||
test "handles high volume of packets efficiently" do
|
test "handles high volume of packets efficiently" do
|
||||||
bounds = %{north: 90.0, south: -90.0, east: 180.0, west: -180.0}
|
bounds = %{north: 90.0, south: -90.0, east: 180.0, west: -180.0}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue