diff --git a/lib/poa_backend/receivers/dashboard.ex b/lib/poa_backend/receivers/dashboard.ex index e54fead..9f13871 100644 --- a/lib/poa_backend/receivers/dashboard.ex +++ b/lib/poa_backend/receivers/dashboard.ex @@ -23,6 +23,11 @@ defmodule POABackend.Receivers.Dashboard do use POABackend.Receiver + # we store the last metrics in a ETS table because when a dashboard is connected it can wait a lot until + # the stats get updated + + @last_metrics_table :last_metrics_table + alias __MODULE__ alias POABackend.Protocol.Message @@ -95,6 +100,8 @@ defmodule POABackend.Receivers.Dashboard do def init_receiver(opts) do :ok = start_websockets_server(opts) + :ok = set_up_last_metrics_table() + {:ok, %{clients: []}} end @@ -105,6 +112,17 @@ defmodule POABackend.Receivers.Dashboard do end def handle_message({:add_client, client}, %{clients: clients} = state) do + + # we send the latest metrics in order to catch up + stored_metrics = :ets.tab2list(@last_metrics_table) + + for {_, metrics} <- stored_metrics do + metrics_list = Map.to_list(metrics) + for {_, metric} <- metrics_list do + send(client, metric) + end + end + {:ok, %{state | clients: [client | clients]}} end @@ -163,6 +181,8 @@ defmodule POABackend.Receivers.Dashboard do end end + save_metrics(metrics) + :ok end @@ -170,4 +190,49 @@ defmodule POABackend.Receivers.Dashboard do dispatch_metric([metric], clients) end + defp save_metrics(metrics) do + for metric <- metrics do + case metric.data["type"] do + "information" -> + save_last_information(metric) + "statistics" -> + save_last_stats(metric) + _ -> + :continue + end + end + + :ok + end + + defp save_last_information(%Message{} = metric) do + case :ets.lookup(@last_metrics_table, metric.agent_id) do + [] -> + :ets.insert(@last_metrics_table, {metric.agent_id, %{information: metric}}) + [{_, last_metrics}] -> + last_metrics = Map.put(last_metrics, :information, metric) + :ets.insert(@last_metrics_table, {metric.agent_id, last_metrics}) + end + + :ok + end + + defp save_last_stats(%Message{} = metric) do + case :ets.lookup(@last_metrics_table, metric.agent_id) do + [] -> + :ets.insert(@last_metrics_table, {metric.agent_id, %{stats: metric}}) + [{_, last_metrics}] -> + last_metrics = Map.put(last_metrics, :stats, metric) + :ets.insert(@last_metrics_table, {metric.agent_id, last_metrics}) + end + + :ok + end + + defp set_up_last_metrics_table do + :ets.new(@last_metrics_table, [:named_table]) + + :ok + end + end \ No newline at end of file diff --git a/test/receivers/dashboard_test.exs b/test/receivers/dashboard_test.exs index 91bef09..80fdd08 100644 --- a/test/receivers/dashboard_test.exs +++ b/test/receivers/dashboard_test.exs @@ -8,8 +8,9 @@ defmodule Receivers.DashboardTest do defmodule Client do use WebSockex - def send(client, message) do + def send(client, message, caller) do WebSockex.send_frame(client, {:text, message}) + Kernel.send(caller, :message_sent) end def start_link(address, state, opts \\ []) do @@ -45,10 +46,32 @@ defmodule Receivers.DashboardTest do assert_receive ^expected_message, 20_000 end + test "When the Dashboard connects already exists last metrics in the Backend" do + + # we send the metrics twice in order to complete all the cases in the Receiver + # when the ets doesn't have the agentid and when it already exists + POABackend.Metric.add(:ethereum_metrics, [information_message(), stats_message(), information_message()]) + POABackend.Metric.add(:ethereum_metrics, [information_message2(), stats_message2()]) + + Client.start_link("http://localhost:8181/ws", self(), [{:extra_headers, [{"wssecret", "mywssecret"}]}]) + + expected_information_message = expected_information_message() + expected_stats_message = expected_stats_message() + expected_information_message2 = expected_information_message2() + expected_stats_message2 = expected_stats_message2() + + assert_receive ^expected_information_message, 20_000 + assert_receive ^expected_stats_message, 20_000 + assert_receive ^expected_information_message2, 20_000 + assert_receive ^expected_stats_message2, 20_000 + end + test "handle messages from the client to the server (test coverage)" do {:ok, client} = Client.start_link("http://localhost:8181/ws", self(), [{:extra_headers, [{"wssecret", "mywssecret"}]}]) - Client.send(client, "hello") + Client.send(client, "hello", self()) + + assert_receive :message_sent, 20_000 end test "http call to the server (test coverage)" do @@ -65,4 +88,35 @@ defmodule Receivers.DashboardTest do Message.new("agentid1", :ethereum_metric, :data, %{a: "a", b: "b", c: "c"}) end + defp information_message do + Message.new("agentid", :ethereum_metric, :data, %{"type" => "information"}) + end + + defp stats_message do + Message.new("agentid", :ethereum_metric, :data, %{"type" => "statistics"}) + end + + defp information_message2 do + Message.new("agentid2", :ethereum_metric, :data, %{"type" => "information"}) + end + + defp stats_message2 do + Message.new("agentid2", :ethereum_metric, :data, %{"type" => "statistics"}) + end + + defp expected_information_message do + "{\"data\":{\"type\":\"information\"},\"agent_id\":\"agentid\"}" + end + + defp expected_stats_message do + "{\"data\":{\"type\":\"statistics\"},\"agent_id\":\"agentid\"}" + end + + defp expected_information_message2 do + "{\"data\":{\"type\":\"information\"},\"agent_id\":\"agentid2\"}" + end + + defp expected_stats_message2 do + "{\"data\":{\"type\":\"statistics\"},\"agent_id\":\"agentid2\"}" + end end \ No newline at end of file diff --git a/test/receivers/receivers_test.exs b/test/receivers/receivers_test.exs index e33dfe4..be58dcc 100644 --- a/test/receivers/receivers_test.exs +++ b/test/receivers/receivers_test.exs @@ -1,6 +1,8 @@ defmodule Receivers.ReceiversTest do use ExUnit.Case + alias POABackend.Protocol.Message + test "__using__ Receiver" do defmodule Receiver1 do use POABackend.Receiver @@ -64,15 +66,19 @@ defmodule Receivers.ReceiversTest do {:ok, _} = Receiver2.start_link(state) - POABackend.Metric.add(:ethereum_metrics, [:message1, :message2]) - POABackend.Metric.add(:ethereum_metrics, :message3) + message1 = Message.new("agentID", :ethereum_metric, :data, %{"data" => :nodata}) + message2 = Message.new("agentID2", :ethereum_metric, :data, %{"data" => :nodata}) + message3 = Message.new("agentID3", :ethereum_metric, :data, %{"data" => :nodata}) + + POABackend.Metric.add(:ethereum_metrics, [message1, message2]) + POABackend.Metric.add(:ethereum_metrics, message3) metrics_pid = Process.whereis(:ethereum_metrics) send(metrics_pid, :nothing_happens) - assert_receive {:metric_received, :message1}, 20_000 - assert_receive {:metric_received, :message2}, 20_000 - assert_receive {:metric_received, :message3}, 20_000 + assert_receive {:metric_received, ^message1}, 20_000 + assert_receive {:metric_received, ^message2}, 20_000 + assert_receive {:metric_received, ^message3}, 20_000 end end \ No newline at end of file