From 7badb90cec6255d08bbfe53803596e86f21cce60 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Fri, 27 Sep 2024 08:38:26 -0400 Subject: [PATCH] feat: add detour notifications to notifications server (#2815) --- lib/notifications/notification_server.ex | 65 +++++++++++++++++++ .../notification_server_test.exs | 52 ++++++++++++++- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/lib/notifications/notification_server.ex b/lib/notifications/notification_server.ex index 293a95f3d..0b30c01af 100644 --- a/lib/notifications/notification_server.ex +++ b/lib/notifications/notification_server.ex @@ -36,6 +36,29 @@ defmodule Notifications.NotificationServer do GenServer.cast(server, {:bridge_movement, bridge_movement}) end + @doc """ + Creates a new Detour Activated Notification for a `Skate.Detours.Db.Detour`. + + ## Options + + If the `:server` option is present, the notification is sent to the process + referred to by the `:server` value. + + If the `:notify_finished` option is present, a `{:new_notification, detour: detour.id}` message + is sent to the process referred to by the `:notify_finished` value. + This option has mainly been useful for testing code to avoid `Process.sleep()` calls. + """ + @spec detour_activated(detour :: Skate.Detours.Db.Detour.t(), keyword()) :: :ok + def detour_activated( + %Skate.Detours.Db.Detour{} = detour, + options \\ [] + ) do + server = Keyword.get(options, :server, default_name()) + notify_finished = Keyword.get(options, :notify_finished, nil) + + GenServer.cast(server, {:detour_activated, detour, notify_finished}) + end + def subscribe(user_id, server \\ default_name()) do registry_key = GenServer.call(server, :subscribe) @@ -70,6 +93,33 @@ defmodule Notifications.NotificationServer do {:noreply, state} end + @impl true + def handle_cast( + { + :detour_activated, + %Skate.Detours.Db.Detour{id: id} = detour, + notify_finished_caller_id + }, + state + ) do + detour + |> Notifications.Notification.create_activated_detour_notification_from_detour() + |> broadcast(self()) + + notify_caller_new_notification(notify_finished_caller_id, detour: id) + + {:noreply, state} + end + + # Tell the caller when a notification is created + # Mainly useful for writing tests so that they don't require + # `Process.sleep()` + defp notify_caller_new_notification(nil = _caller_id, _value), do: nil + + defp notify_caller_new_notification(caller_id, value) do + send(caller_id, {:new_notification, value}) + end + @spec convert_new_block_waivers_to_notifications([BlockWaiver.t()]) :: [ Notification.t() ] @@ -198,6 +248,21 @@ defmodule Notifications.NotificationServer do broadcast(notification, User.user_ids_for_route_ids(@chelsea_bridge_route_ids), registry_key) end + defp broadcast( + %Notifications.Notification{ + content: %Notifications.Db.Detour{} + } = notification, + registry_key + ) do + n = {:notification, default_unread(notification)} + + Registry.dispatch(Notifications.Supervisor.registry_name(), registry_key, fn entries -> + for {pid, _user_id} <- entries do + send(pid, n) + end + end) + end + defp broadcast( %Notifications.Notification{ content: %Notifications.Db.BlockWaiver{ diff --git a/test/notifications/notification_server_test.exs b/test/notifications/notification_server_test.exs index e6678d3c2..055ab0067 100644 --- a/test/notifications/notification_server_test.exs +++ b/test/notifications/notification_server_test.exs @@ -218,7 +218,7 @@ defmodule Notifications.NotificationServerTest do start_supervised({Registry, keys: :duplicate, name: registry_name}) reassign_env(:notifications, :registry, registry_name) - {:ok, server} = NotificationServer.start_link(name: :new_notifications) + {:ok, server} = NotificationServer.start_link(name: __MODULE__) if user_id do NotificationServer.subscribe(user_id, server) @@ -613,4 +613,54 @@ defmodule Notifications.NotificationServerTest do refute String.contains?(log, "new_notification") end end + + describe "detour_activated/2" do + setup do + {:ok, server} = setup_server() + + %{server: server} + end + + test "saves to database" do + notification_count = 3 + # create new notification + for _ <- 1..notification_count do + %{id: id} = + detour = + insert(:detour) + + NotificationServer.detour_activated(detour, notify_finished: self(), server: __MODULE__) + + assert_receive {:new_notification, detour: ^id} + end + + # assert database contains notification + assert_n_notifications_in_db(notification_count) + end + + test "broadcasts to all connected users", %{server: server} do + notification_count = 3 + # connect users to channel + for id <- 1..notification_count do + NotificationServer.subscribe(id, server) + end + + # create new notification + %{id: id} = + detour = + insert(:detour) + + NotificationServer.detour_activated(detour, notify_finished: self(), server: __MODULE__) + + assert_receive {:new_notification, detour: ^id} + + # assert channel sends notifications to each user + for _ <- 1..notification_count do + assert_receive {:notification, + %Notification{ + content: %Notifications.Db.Detour{} + }} + end + end + end end