defmodule <%= inspect @controller_module %> do @moduledoc """ Generated by Caravela from <%= inspect @domain_module %>. JSON controller for `<%= @entity_name %>`. All actions build a `context` map from `conn.assigns` and delegate to `<%= inspect @context_module %>`. Regenerate with `mix caravela.gen.api <%= inspect @domain_module %>`. Custom code placed below the `# --- CUSTOM ---` marker is preserved. """ use Phoenix.Controller, formats: [:json] alias <%= inspect @context_module %> def index(conn, _params) do context = build_context(conn) items = <%= @context_short %>.<%= @list_fn %>(context) json(conn, %{data: Enum.map(items, &serialize/1)}) end def show(conn, %{"id" => id}) do context = build_context(conn) case <%= @context_short %>.<%= @get_fn %>(id, context) do nil -> send_not_found(conn) entity -> json(conn, %{data: serialize(entity)}) end end def create(conn, params) do context = build_context(conn) attrs = Map.get(params, "<%= @singular %>", Map.drop(params, ["id"])) case <%= @context_short %>.<%= @create_fn %>(attrs, context) do {:ok, entity} -> conn |> put_status(:created) |> json(%{data: serialize(entity)}) {:error, :unauthorized} -> send_unauthorized(conn) {:error, %Ecto.Changeset{} = changeset} -> send_changeset_error(conn, changeset) {:error, reason} -> send_error(conn, reason) end end def update(conn, %{"id" => id} = params) do context = build_context(conn) attrs = Map.get(params, "<%= @singular %>", Map.drop(params, ["id"])) case <%= @context_short %>.<%= @get_fn %>(id, context) do nil -> send_not_found(conn) entity -> case <%= @context_short %>.<%= @update_fn %>(entity, attrs, context) do {:ok, updated} -> json(conn, %{data: serialize(updated)}) {:error, :unauthorized} -> send_unauthorized(conn) {:error, %Ecto.Changeset{} = cs} -> send_changeset_error(conn, cs) {:error, reason} -> send_error(conn, reason) end end end def delete(conn, %{"id" => id}) do context = build_context(conn) case <%= @context_short %>.<%= @get_fn %>(id, context) do nil -> send_not_found(conn) entity -> case <%= @context_short %>.<%= @delete_fn %>(entity, context) do {:ok, _} -> send_resp(conn, :no_content, "") {:error, :unauthorized} -> send_unauthorized(conn) {:error, reason} -> send_error(conn, reason) end end end # --- Internal -------------------------------------------------------- defp build_context(conn) do %{ current_user: conn.assigns[:current_user],<%= if @multi_tenant do %> tenant: conn.assigns[:tenant],<% end %> conn: conn } end defp serialize(%_{} = entity) do entity |> Map.from_struct() |> Map.drop([:__meta__]) |> Enum.reject(fn {_, v} -> match?(%Ecto.Association.NotLoaded{}, v) end) |> Map.new() end defp send_not_found(conn), do: conn |> put_status(:not_found) |> json(%{error: "not_found"}) defp send_unauthorized(conn), do: conn |> put_status(:forbidden) |> json(%{error: "unauthorized"}) defp send_error(conn, reason), do: conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)}) defp send_changeset_error(conn, %Ecto.Changeset{} = changeset) do errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} -> Enum.reduce(opts, msg, fn {k, v}, acc -> String.replace(acc, "%{#{k}}", to_string(v)) end) end) conn |> put_status(:unprocessable_entity) |> json(%{errors: errors}) end <%= @custom_marker %>