towerops/lib/towerops_web/controllers/api/v1/alerts_controller.ex
Graham McIntire 146f5745cf
fix: resolve compilation errors, test failures, and credo issues
- Escape HEEx template braces in GraphQL/API docs with raw(~S[...])
- Fix test assertions for updated marketing copy and UI text
- Extract helper functions in GraphQL resolvers to reduce nesting depth
- Create shared ErrorHelpers module for API controllers
- Fix ETS race condition in brute force whitelist cache for async tests
- Fix property test generators to use ASCII instead of printable unicode
- Add alert_severity helper to site_live/show
- Update accounts fixtures for explicit user confirmation
2026-02-14 12:23:10 -06:00

114 lines
3.3 KiB
Elixir

defmodule ToweropsWeb.Api.V1.AlertsController do
@moduledoc "API controller for alerts."
use ToweropsWeb, :controller
alias Towerops.Alerts
def index(conn, params) do
organization_id = conn.assigns.current_organization_id
filters =
params
|> Map.take(~w(status limit))
|> then(fn f ->
case params["device_id"] do
nil -> f
device_id -> Map.put(f, "device_id", device_id)
end
end)
alerts =
organization_id
|> Alerts.list_organization_alerts(filters)
|> Enum.map(&format_alert/1)
json(conn, %{data: alerts})
end
def show(conn, %{"id" => id}) do
organization_id = conn.assigns.current_organization_id
alert = Alerts.get_alert!(id)
device = alert.device
if device && device.organization_id == organization_id do
json(conn, %{data: format_alert(alert)})
else
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
rescue
Ecto.NoResultsError ->
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
def acknowledge(conn, %{"id" => id}) do
organization_id = conn.assigns.current_organization_id
user = conn.assigns[:current_user]
alert = Alerts.get_alert!(id)
device = alert.device
if device && device.organization_id == organization_id do
user_id = if user, do: user.id
case Alerts.acknowledge_alert(alert, user_id) do
{:ok, updated} ->
json(conn, %{data: format_alert(Towerops.Repo.preload(updated, [:device, :acknowledged_by], force: true))})
{:error, _changeset} ->
conn |> put_status(:unprocessable_entity) |> json(%{error: "Could not acknowledge alert"})
end
else
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
rescue
Ecto.NoResultsError ->
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
def resolve(conn, %{"id" => id}) do
organization_id = conn.assigns.current_organization_id
alert = Alerts.get_alert!(id)
device = alert.device
if device && device.organization_id == organization_id do
case Alerts.resolve_alert(alert) do
{:ok, updated} ->
json(conn, %{data: format_alert(Towerops.Repo.preload(updated, [:device, :acknowledged_by], force: true))})
{:error, _changeset} ->
conn |> put_status(:unprocessable_entity) |> json(%{error: "Could not resolve alert"})
end
else
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
rescue
Ecto.NoResultsError ->
conn |> put_status(:not_found) |> json(%{error: "Alert not found"})
end
defp format_alert(alert) do
%{
id: alert.id,
alert_type: alert.alert_type,
message: alert.message,
triggered_at: alert.triggered_at,
acknowledged_at: alert.acknowledged_at,
resolved_at: alert.resolved_at,
device_id: alert.device_id,
device_name: get_in_loaded(alert, :device, :name),
acknowledged_by_email: get_in_loaded(alert, :acknowledged_by, :email),
gaiia_impact: alert.gaiia_impact,
inserted_at: alert.inserted_at
}
end
defp get_in_loaded(struct, assoc, field) do
case Map.get(struct, assoc) do
%Ecto.Association.NotLoaded{} -> nil
nil -> nil
loaded -> Map.get(loaded, field)
end
end
end