towerops/lib/towerops_web/controllers/api/v1/check_results_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

132 lines
3.8 KiB
Elixir

defmodule ToweropsWeb.Api.V1.CheckResultsController do
@moduledoc "API controller for device checks, metrics, and interfaces."
use ToweropsWeb, :controller
alias Towerops.Devices
alias Towerops.Monitoring
alias Towerops.Snmp
def checks(conn, %{"device_id" => device_id}) do
organization_id = conn.assigns.current_organization_id
case get_org_device(device_id, organization_id) do
{:ok, device} ->
checks =
organization_id
|> Monitoring.list_checks(device_id: device.id)
|> Enum.map(fn check ->
latest = Monitoring.get_latest_check_result(check.id)
%{
id: check.id,
name: check.name,
check_type: check.check_type,
enabled: check.enabled,
interval_seconds: check.interval_seconds,
latest_status: latest && latest.status,
latest_value: latest && latest.value,
latest_checked_at: latest && latest.checked_at
}
end)
json(conn, %{data: checks})
{:error, status} ->
conn |> put_status(status) |> json(%{error: "Device not found"})
end
end
def metrics(conn, %{"device_id" => device_id} = params) do
organization_id = conn.assigns.current_organization_id
case get_org_device(device_id, organization_id) do
{:ok, device} ->
check_type = params["sensor_type"] || params["check_type"]
hours = parse_int(params["hours"], 24)
from_time = DateTime.add(DateTime.utc_now(), -hours * 3600, :second)
checks = Monitoring.list_checks(organization_id, device_id: device.id, check_type: check_type)
metrics =
Enum.map(checks, fn check ->
results = Monitoring.get_check_results(check.id, from: from_time)
%{
check_id: check.id,
check_name: check.name,
check_type: check.check_type,
data:
Enum.map(results, fn r ->
%{timestamp: r.checked_at, value: r.value, status: r.status}
end)
}
end)
json(conn, %{data: metrics})
{:error, status} ->
conn |> put_status(status) |> json(%{error: "Device not found"})
end
end
def interfaces(conn, %{"device_id" => device_id}) do
organization_id = conn.assigns.current_organization_id
case get_org_device(device_id, organization_id) do
{:ok, device} ->
device = Towerops.Repo.preload(device, :snmp_device)
interfaces =
case device.snmp_device do
nil -> []
snmp_device -> format_interfaces(snmp_device)
end
json(conn, %{data: interfaces})
{:error, status} ->
conn |> put_status(status) |> json(%{error: "Device not found"})
end
end
defp get_org_device(device_id, organization_id) do
device = Devices.get_device!(device_id)
if device.organization_id == organization_id do
{:ok, device}
else
{:error, :forbidden}
end
rescue
Ecto.NoResultsError -> {:error, :not_found}
end
defp parse_int(nil, default), do: default
defp parse_int(val, default) when is_binary(val) do
case Integer.parse(val) do
{n, _} -> n
:error -> default
end
end
defp parse_int(val, _default) when is_integer(val), do: val
defp format_interfaces(snmp_device) do
snmp_device.id
|> Snmp.list_interfaces()
|> Enum.map(fn iface ->
%{
id: iface.id,
if_index: iface.if_index,
if_name: iface.if_name,
if_descr: iface.if_descr,
if_alias: iface.if_alias,
if_speed: iface.if_speed,
if_admin_status: iface.if_admin_status,
if_oper_status: iface.if_oper_status,
monitored: iface.monitored
}
end)
end
end