towerops/lib/towerops_web/controllers/api/v1/maintenance_windows_controller.ex
Graham McIntire 17fa005781
add maintenance windows REST API and GraphQL support
REST endpoints for CRUD, scoped filtering (active/upcoming/past).
GraphQL queries and mutations for maintenance windows.
Fix agent edit test assertion message.
2026-03-11 14:33:25 -05:00

163 lines
4.6 KiB
Elixir

defmodule ToweropsWeb.Api.V1.MaintenanceWindowsController do
@moduledoc """
API controller for managing maintenance windows.
All endpoints require API token authentication and operations are scoped
to the organization associated with the token.
"""
use ToweropsWeb, :controller
import ToweropsWeb.Api.ErrorHelpers, only: [translate_errors: 1]
alias Towerops.Maintenance
alias Towerops.Maintenance.MaintenanceWindow
alias ToweropsWeb.ScopedResource
@doc "GET /api/v1/maintenance_windows"
def index(conn, params) do
organization_id = conn.assigns.current_organization_id
filter = Map.get(params, "filter")
opts =
case filter do
"active" -> [filter: :active]
"upcoming" -> [filter: :upcoming]
"past" -> [filter: :past]
_ -> []
end
windows =
organization_id
|> Maintenance.list_windows(opts)
|> Enum.map(&format_window/1)
json(conn, %{maintenance_windows: windows})
end
@doc "POST /api/v1/maintenance_windows"
def create(conn, %{"maintenance_window" => window_params}) do
organization_id = conn.assigns.current_organization_id
user_id = conn.assigns.current_user.id
attrs =
window_params
|> Map.put("organization_id", organization_id)
|> Map.put("created_by_id", user_id)
case Maintenance.create_window(attrs) do
{:ok, window} ->
conn
|> put_status(:created)
|> json(format_window(window))
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{errors: translate_errors(changeset)})
end
end
def create(conn, _params) do
conn
|> put_status(:bad_request)
|> json(%{error: "Missing 'maintenance_window' parameter"})
end
@doc "GET /api/v1/maintenance_windows/:id"
def show(conn, %{"id" => id}) do
organization_id = conn.assigns.current_organization_id
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
{:ok, window} ->
json(conn, format_window(window))
{:error, :forbidden} ->
conn
|> put_status(:forbidden)
|> json(%{error: "Access denied to this maintenance window"})
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "Maintenance window not found"})
end
end
@doc "PATCH /api/v1/maintenance_windows/:id"
def update(conn, %{"id" => id, "maintenance_window" => window_params}) do
organization_id = conn.assigns.current_organization_id
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
{:ok, window} ->
case Maintenance.update_window(window, window_params) do
{:ok, updated} ->
json(conn, format_window(updated))
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{errors: translate_errors(changeset)})
end
{:error, :forbidden} ->
conn
|> put_status(:forbidden)
|> json(%{error: "Access denied to this maintenance window"})
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "Maintenance window not found"})
end
end
def update(conn, _params) do
conn
|> put_status(:bad_request)
|> json(%{error: "Missing 'maintenance_window' parameter"})
end
@doc "DELETE /api/v1/maintenance_windows/:id"
def delete(conn, %{"id" => id}) do
organization_id = conn.assigns.current_organization_id
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
{:ok, window} ->
case Maintenance.delete_window(window) do
{:ok, _} ->
json(conn, %{success: true})
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{errors: translate_errors(changeset)})
end
{:error, :forbidden} ->
conn
|> put_status(:forbidden)
|> json(%{error: "Access denied to this maintenance window"})
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "Maintenance window not found"})
end
end
defp format_window(window) do
%{
id: window.id,
name: window.name,
reason: window.reason,
starts_at: window.starts_at,
ends_at: window.ends_at,
recurring: window.recurring,
recurrence_rule: window.recurrence_rule,
suppress_alerts: window.suppress_alerts,
site_id: window.site_id,
device_id: window.device_id,
inserted_at: window.inserted_at
}
end
end