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