Skip to content

Commit

Permalink
feat: send detour notification when detour is activated
Browse files Browse the repository at this point in the history
  • Loading branch information
firestack committed Sep 26, 2024
1 parent ead40ab commit 0649891
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 30 deletions.
72 changes: 60 additions & 12 deletions lib/skate/detours/detours.ex
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ defmodule Skate.Detours.Detours do
otherwise returns `nil` if it is a draft but does not belong to the provided
user
"""
@spec categorize_detour(map(), integer()) :: detour_type
@spec categorize_detour(detour :: map(), user_id :: Skate.Settings.Db.User.id()) ::
detour_type() | nil
def categorize_detour(
%{state: %{"value" => %{"Detour Drawing" => %{"Active" => _}}}},
_user_id
Expand Down Expand Up @@ -197,20 +198,67 @@ defmodule Skate.Detours.Detours do
def update_or_create_detour_for_user(user_id, uuid, attrs \\ %{}) do
user = User.get_by_id!(user_id)

case uuid do
nil ->
create_detour_for_user(user_id, attrs)

_ ->
Repo.insert(
Detour.changeset(%Detour{author: user, id: uuid}, attrs),
returning: true,
conflict_target: [:id],
on_conflict: {:replace, [:state, :updated_at]}
)
previous_state = categorize_detour_by_id(uuid, user_id)

detour =
case uuid do
nil ->
create_detour_for_user(user_id, attrs)

_ ->
Repo.insert(
Detour.changeset(%Detour{author: user, id: uuid}, attrs),
returning: true,
conflict_target: [:id],
on_conflict: {:replace, [:state, :updated_at]}
)
end

send_notification(detour, previous_state, user_id)

detour
end

@doc """
Retrieves a `Skate.Detours.Db.Detour` from the database by it's ID and then resolves the
detour's category via `categorize_detour/2`
"""
@spec categorize_detour_by_id(
detour_id :: nil | integer(),
user_id :: Skate.Settings.Db.User.id()
) :: detour_type() | nil
def categorize_detour_by_id(nil = _detour_id, _user_id), do: nil

def categorize_detour_by_id(detour_id, user_id) do
case Skate.Repo.get(Detour, detour_id) do
%Detour{} = detour -> categorize_detour(detour, user_id)
_ -> nil
end
end

@spec send_notification(
detour_db_result :: {:ok, Skate.Detours.Db.Detour.t()},
previous_state :: detour_type(),
user_id :: Skate.Settings.Db.User.t()
) :: :ok | nil
@spec send_notification(
detour_db_result :: any(),
previous_state :: detour_type(),
user_id :: Skate.Settings.Db.User.t()
) :: nil
defp send_notification(
{:ok, %Skate.Detours.Db.Detour{} = detour},
previous_state,
user_id
)
when previous_state != :active do
if categorize_detour(detour, user_id) == :active do
Notifications.NotificationServer.detour_activated(detour)
end
end

defp send_notification(_, _, _), do: nil

@doc """
Deletes a detour.
Expand Down
37 changes: 37 additions & 0 deletions test/skate_web/controllers/detours_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ defmodule SkateWeb.DetoursControllerTest do
} = Detours.get_detour!(number)
end

defp setup_notification_server do
registry_name = :new_notifications_registry
start_supervised({Registry, keys: :duplicate, name: registry_name})
reassign_env(:notifications, :registry, registry_name)

start_link_supervised!(Notifications.NotificationServer)
end

@tag :authenticated
test "updates detour in database if detour uuid provided", %{conn: conn} do
conn =
Expand All @@ -56,6 +64,35 @@ defmodule SkateWeb.DetoursControllerTest do
state: %{"context" => %{"uuid" => 8}}
} = Detours.get_detour!(8)
end

@tag :authenticated
test "creates a new notification when detour is activated", %{conn: conn} do
setup_notification_server()

%Skate.Detours.Db.Detour{id: id, state: snapshot} = insert(:detour)

put(conn, ~p"/api/detours/update_snapshot", %{
"snapshot" => snapshot |> activated |> with_id(id)
})

Process.sleep(10)
assert Skate.Repo.aggregate(Notifications.Db.Detour, :count) == 1
end

@tag :authenticated
test "does not create a new notification if detour was already activated", %{conn: conn} do
setup_notification_server()

%Skate.Detours.Db.Detour{id: id, state: snapshot} =
:detour |> build |> activated |> insert

put(conn, ~p"/api/detours/update_snapshot", %{
"snapshot" => with_id(snapshot, id)
})

Process.sleep(10)
assert Skate.Repo.aggregate(Notifications.Db.Detour, :count) == 0
end
end

defp populate_db_and_get_user(conn) do
Expand Down
68 changes: 50 additions & 18 deletions test/support/factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -416,30 +416,62 @@ defmodule Skate.Factory do
def detour_factory do
%Skate.Detours.Db.Detour{
author: build(:user),
state: %{
"context" => %{
"route" => %{
"name" => sequence("detour_route_name:"),
"directionNames" => %{
"0" => "Outbound",
"1" => "Inbound"
}
},
"routePattern" => %{
"name" => sequence("detour_route_pattern_name:"),
"headsign" => sequence("detour_route_pattern_headsign:"),
"directionId" => sequence(:detour_route_pattern_direction, [0, 1])
state: build(:detour_snapshot)
}
end

def detour_snapshot_factory do
%{
"context" => %{
"uuid" => nil,
"route" => %{
"name" => sequence("detour_route_name:"),
"directionNames" => %{
"0" => "Outbound",
"1" => "Inbound"
}
},
"routePattern" => %{
"name" => sequence("detour_route_pattern_name:"),
"headsign" => sequence("detour_route_pattern_headsign:"),
"directionId" => sequence(:detour_route_pattern_direction, [0, 1])
}
}
},
"value" => %{}
}
end

def with_id(%Skate.Detours.Db.Detour{} = detour, id) do
%{detour | state: with_id(detour.state, id)}
end

def with_id(%{"context" => %{"uuid" => _}} = snapshot, id) do
put_in(snapshot["context"]["uuid"], id)
end

def activated(%Skate.Detours.Db.Detour{} = detour) do
%{detour | state: activated(detour.state)}
end

def activated(%{"value" => %{}} = state) do
put_in(state["value"], %{"Detour Drawing" => %{"Active" => "Reviewing"}})
end

def with_direction(%Skate.Detours.Db.Detour{} = detour, direction) do
%{
detour
| state: with_direction(detour.state, direction)
}
end

def with_direction(%Skate.Detours.Db.Detour{} = detour, :inbound) do
put_in(detour.state["context"]["routePattern"]["directionId"], 1)
def with_direction(%{"context" => %{"routePattern" => %{"directionId" => _}}} = state, :inbound) do
put_in(state["context"]["routePattern"]["directionId"], 1)
end

def with_direction(%Skate.Detours.Db.Detour{} = detour, :outbound) do
put_in(detour.state["context"]["routePattern"]["directionId"], 0)
def with_direction(
%{"context" => %{"routePattern" => %{"directionId" => _}}} = state,
:outbound
) do
put_in(state["context"]["routePattern"]["directionId"], 0)
end
end

0 comments on commit 0649891

Please sign in to comment.