REST API v1 endpoints for schedules (14 endpoints) and escalation policies (10 endpoints) with full CRUD for nested resources (layers, members, overrides, rules, targets). GraphQL types, resolvers, and schema mutations for both resource trees.
223 lines
7.2 KiB
Elixir
223 lines
7.2 KiB
Elixir
defmodule ToweropsWeb.GraphQL.Resolvers.Schedule do
|
|
@moduledoc "GraphQL resolvers for on-call schedule queries and mutations."
|
|
|
|
alias Towerops.OnCall
|
|
alias Towerops.OnCall.Layer
|
|
alias Towerops.OnCall.LayerMember
|
|
alias Towerops.OnCall.Override
|
|
alias Towerops.OnCall.Schedule
|
|
alias Towerops.Repo
|
|
alias ToweropsWeb.GraphQL.Resolvers.Helpers
|
|
alias ToweropsWeb.ScopedResource
|
|
|
|
# --- Queries ---
|
|
|
|
def list(_parent, _args, %{context: %{organization_id: org_id}}) do
|
|
{:ok, OnCall.list_schedules(org_id)}
|
|
end
|
|
|
|
def list(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def get(_parent, %{id: id}, %{context: %{organization_id: org_id}}) do
|
|
case ScopedResource.fetch_preload(Schedule, id, org_id,
|
|
overrides: :user,
|
|
layers: [members: :user]
|
|
) do
|
|
{:ok, schedule} -> {:ok, schedule}
|
|
{:error, _} -> {:error, "Schedule not found"}
|
|
end
|
|
end
|
|
|
|
def get(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def on_call(_parent, %{schedule_id: id}, %{context: %{organization_id: org_id}}) do
|
|
case ScopedResource.fetch(Schedule, id, org_id) do
|
|
{:ok, _} ->
|
|
case OnCall.who_is_on_call(id) do
|
|
nil -> {:ok, nil}
|
|
user -> {:ok, %{user_id: user.id, email: user.email}}
|
|
end
|
|
|
|
{:error, _} ->
|
|
{:error, "Schedule not found"}
|
|
end
|
|
end
|
|
|
|
def on_call(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
# --- Mutations ---
|
|
|
|
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 OnCall.create_schedule(attrs) do
|
|
{:ok, schedule} -> {:ok, schedule}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
|
|
def create(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def update(_parent, %{id: id, input: input}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, schedule} <- fetch_org_schedule(id, org_id) do
|
|
attrs = Map.new(input, fn {k, v} -> {to_string(k), v} end)
|
|
|
|
case OnCall.update_schedule(schedule, attrs) do
|
|
{:ok, updated} -> {:ok, updated}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
end
|
|
|
|
def update(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def delete(_parent, %{id: id}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, schedule} <- fetch_org_schedule(id, org_id) do
|
|
case OnCall.delete_schedule(schedule) do
|
|
{:ok, _} -> {:ok, %{success: true, message: "Schedule deleted"}}
|
|
{:error, _} -> {:ok, %{success: false, message: "Could not delete schedule"}}
|
|
end
|
|
end
|
|
end
|
|
|
|
def delete(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
# --- Layer mutations ---
|
|
|
|
def create_layer(_parent, %{schedule_id: schedule_id, input: input}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, _schedule} <- fetch_org_schedule(schedule_id, org_id) do
|
|
attrs =
|
|
input
|
|
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
|
|> Map.put("schedule_id", schedule_id)
|
|
|
|
case OnCall.create_layer(attrs) do
|
|
{:ok, layer} -> {:ok, layer}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
end
|
|
|
|
def create_layer(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def update_layer(_parent, %{id: id, input: input}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, layer} <- fetch_layer_by_org(id, org_id) do
|
|
attrs = Map.new(input, fn {k, v} -> {to_string(k), v} end)
|
|
|
|
case OnCall.update_layer(layer, attrs) do
|
|
{:ok, updated} -> {:ok, updated}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
end
|
|
|
|
def update_layer(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def delete_layer(_parent, %{id: id}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, layer} <- fetch_layer_by_org(id, org_id) do
|
|
case OnCall.delete_layer(layer) do
|
|
{:ok, _} -> {:ok, %{success: true, message: "Layer deleted"}}
|
|
{:error, _} -> {:ok, %{success: false, message: "Could not delete layer"}}
|
|
end
|
|
end
|
|
end
|
|
|
|
def delete_layer(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
# --- Member mutations ---
|
|
|
|
def add_member(_parent, %{layer_id: layer_id, user_id: user_id, position: position}, %{
|
|
context: %{organization_id: org_id}
|
|
}) do
|
|
with {:ok, _layer} <- fetch_layer_by_org(layer_id, org_id) do
|
|
attrs = %{
|
|
"layer_id" => layer_id,
|
|
"user_id" => user_id,
|
|
"position" => position
|
|
}
|
|
|
|
case OnCall.add_layer_member(attrs) do
|
|
{:ok, member} -> {:ok, member}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
end
|
|
|
|
def add_member(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def remove_member(_parent, %{id: id}, %{context: %{organization_id: _org_id}}) do
|
|
case Repo.get(LayerMember, id) do
|
|
nil ->
|
|
{:error, "Member not found"}
|
|
|
|
member ->
|
|
case OnCall.remove_layer_member(member) do
|
|
{:ok, _} -> {:ok, %{success: true, message: "Member removed"}}
|
|
{:error, _} -> {:ok, %{success: false, message: "Could not remove member"}}
|
|
end
|
|
end
|
|
end
|
|
|
|
def remove_member(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
# --- Override mutations ---
|
|
|
|
def create_override(_parent, %{schedule_id: schedule_id, input: input}, %{context: %{organization_id: org_id}}) do
|
|
with {:ok, _schedule} <- fetch_org_schedule(schedule_id, org_id) do
|
|
attrs =
|
|
input
|
|
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
|
|> Map.put("schedule_id", schedule_id)
|
|
|
|
case OnCall.create_override(attrs) do
|
|
{:ok, override} -> {:ok, override}
|
|
{:error, changeset} -> {:error, Helpers.format_changeset_errors(changeset)}
|
|
end
|
|
end
|
|
end
|
|
|
|
def create_override(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
def delete_override(_parent, %{id: id}, %{context: %{organization_id: _org_id}}) do
|
|
case Repo.get(Override, id) do
|
|
nil ->
|
|
{:error, "Override not found"}
|
|
|
|
override ->
|
|
case OnCall.delete_override(override) do
|
|
{:ok, _} -> {:ok, %{success: true, message: "Override deleted"}}
|
|
{:error, _} -> {:ok, %{success: false, message: "Could not delete override"}}
|
|
end
|
|
end
|
|
end
|
|
|
|
def delete_override(_parent, _args, _resolution), do: Helpers.authentication_error()
|
|
|
|
# --- Private helpers ---
|
|
|
|
defp fetch_org_schedule(id, org_id) do
|
|
case ScopedResource.fetch(Schedule, id, org_id) do
|
|
{:ok, schedule} -> {:ok, schedule}
|
|
{:error, _} -> {:error, "Schedule not found"}
|
|
end
|
|
end
|
|
|
|
defp fetch_layer_by_org(layer_id, org_id) do
|
|
case Repo.get(Layer, layer_id) do
|
|
nil ->
|
|
{:error, "Layer not found"}
|
|
|
|
layer ->
|
|
layer = Repo.preload(layer, :schedule)
|
|
|
|
if layer.schedule.organization_id == org_id do
|
|
{:ok, layer}
|
|
else
|
|
{:error, "Layer not found"}
|
|
end
|
|
end
|
|
end
|
|
end
|