- Replace terse helper text with step-by-step setup guide in config form - Add what-gets-synced breakdown when PagerDuty is enabled - Clear visual hierarchy with numbered steps and inline callout
167 lines
5.1 KiB
Elixir
167 lines
5.1 KiB
Elixir
defmodule ToweropsWeb.GraphQL.Resolvers.Device do
|
|
@moduledoc "GraphQL resolvers for device queries and mutations."
|
|
|
|
alias Towerops.Devices
|
|
alias Towerops.Monitoring
|
|
alias Towerops.Repo
|
|
alias Towerops.Snmp
|
|
|
|
def list(_parent, args, %{context: %{organization_id: org_id}}) do
|
|
params = args |> Map.new(fn {k, v} -> {to_string(k), v} end)
|
|
devices = Devices.list_organization_devices(org_id, params)
|
|
{:ok, devices}
|
|
end
|
|
|
|
def list(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def get(_parent, %{id: id}, %{context: %{organization_id: org_id}}) do
|
|
case Repo.get(Towerops.Devices.Device, id) do
|
|
nil ->
|
|
{:error, "Device not found"}
|
|
|
|
device ->
|
|
if device.organization_id == org_id do
|
|
{:ok, Repo.preload(device, :site)}
|
|
else
|
|
{:error, "Device not found"}
|
|
end
|
|
end
|
|
end
|
|
|
|
def get(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def create(_parent, %{input: input}, %{context: %{organization_id: org_id}}) do
|
|
attrs =
|
|
input
|
|
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
|
|> Map.put("organization_id", org_id)
|
|
|
|
case Devices.create_device(attrs) do
|
|
{:ok, device} -> {:ok, Repo.preload(device, :site)}
|
|
{:error, changeset} -> {:error, format_errors(changeset)}
|
|
end
|
|
end
|
|
|
|
def create(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def update(_parent, %{id: id, input: input}, %{context: %{organization_id: org_id}}) do
|
|
case Repo.get(Towerops.Devices.Device, id) do
|
|
nil ->
|
|
{:error, "Device not found"}
|
|
|
|
device ->
|
|
if device.organization_id == org_id do
|
|
attrs = Map.new(input, fn {k, v} -> {to_string(k), v} end)
|
|
|
|
case Devices.update_device(device, attrs) do
|
|
{:ok, updated} -> {:ok, Repo.preload(updated, :site, force: true)}
|
|
{:error, changeset} -> {:error, format_errors(changeset)}
|
|
end
|
|
else
|
|
{:error, "Device not found"}
|
|
end
|
|
end
|
|
end
|
|
|
|
def update(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def delete(_parent, %{id: id}, %{context: %{organization_id: org_id}}) do
|
|
case Repo.get(Towerops.Devices.Device, id) do
|
|
nil ->
|
|
{:error, "Device not found"}
|
|
|
|
device ->
|
|
if device.organization_id == org_id do
|
|
case Devices.delete_device(device) do
|
|
{:ok, _} -> {:ok, %{success: true, message: "Device deleted"}}
|
|
{:error, _} -> {:ok, %{success: false, message: "Could not delete device"}}
|
|
end
|
|
else
|
|
{:error, "Device not found"}
|
|
end
|
|
end
|
|
end
|
|
|
|
def delete(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def metrics(_parent, %{device_id: device_id} = args, %{context: %{organization_id: org_id}}) do
|
|
case Repo.get(Towerops.Devices.Device, device_id) do
|
|
nil ->
|
|
{:error, "Device not found"}
|
|
|
|
device ->
|
|
if device.organization_id == org_id do
|
|
hours = parse_time_range(Map.get(args, :time_range, "24h"))
|
|
from_time = DateTime.add(DateTime.utc_now(), -hours * 3600, :second)
|
|
check_type = args[:sensor_type]
|
|
|
|
checks = Monitoring.list_checks(org_id, device_id: device.id, check_type: check_type)
|
|
|
|
metrics =
|
|
Enum.flat_map(checks, fn check ->
|
|
results = Monitoring.get_check_results(check.id, from: from_time)
|
|
|
|
Enum.map(results, fn r ->
|
|
%{
|
|
timestamp: to_string(r.checked_at),
|
|
value: r.value,
|
|
status: r.status,
|
|
check_name: check.name,
|
|
check_type: check.check_type
|
|
}
|
|
end)
|
|
end)
|
|
|
|
{:ok, metrics}
|
|
else
|
|
{:error, "Device not found"}
|
|
end
|
|
end
|
|
end
|
|
|
|
def metrics(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
def interfaces(_parent, %{device_id: device_id}, %{context: %{organization_id: org_id}}) do
|
|
case Repo.get(Towerops.Devices.Device, device_id) do
|
|
nil ->
|
|
{:error, "Device not found"}
|
|
|
|
device ->
|
|
if device.organization_id == org_id do
|
|
device = Repo.preload(device, :snmp_device)
|
|
|
|
interfaces =
|
|
case device.snmp_device do
|
|
nil ->
|
|
[]
|
|
|
|
snmp_device ->
|
|
Snmp.list_interfaces(snmp_device.id)
|
|
end
|
|
|
|
{:ok, interfaces}
|
|
else
|
|
{:error, "Device not found"}
|
|
end
|
|
end
|
|
end
|
|
|
|
def interfaces(_parent, _args, _resolution), do: {:error, "Authentication required"}
|
|
|
|
defp parse_time_range(range) do
|
|
case Integer.parse(String.replace(range, "h", "")) do
|
|
{hours, _} -> hours
|
|
:error -> 24
|
|
end
|
|
end
|
|
|
|
defp format_errors(changeset) do
|
|
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
|
|
Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
|
|
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
|
|
end)
|
|
end)
|
|
|> Enum.map(fn {field, messages} -> "#{field}: #{Enum.join(messages, ", ")}" end)
|
|
|> Enum.join("; ")
|
|
end
|
|
end
|