Current section

Files

Jump to
caravela priv templates context.eex
Raw

priv/templates/context.eex

defmodule <%= inspect @context_module %> do
@moduledoc """
Generated by Caravela from <%= inspect @domain_module %>.
CRUD context with hooks and standalone authorization.<%= if @multi_tenant do %>
Multi-tenant: every read is scoped by `tenant_id` and every write
stamps `tenant_id` from the caller's `context.tenant`.<% end %>
Regenerate with `mix caravela.gen.context <%= inspect @domain_module %>`.
Custom code placed below the `# --- CUSTOM ---` marker is preserved.
"""
<%= if @multi_tenant do %> import Ecto.Query, only: [where: 3]
<% end %>
alias <%= inspect @repo_module %>
<%= for e <- @entities do %> alias <%= inspect e.module %>
<% end %>
<%= for e <- @entities do %>
@doc "List `<%= e.plural %>` the caller is allowed to read."
def <%= e.list_fn %>(context \\ %{}) do
<%= e.module_short %>
|> scope_tenant(context)
|> apply_read_permission(<%= inspect e.entity_name %>, context)
|> Repo.all()
end
@doc "Fetch a single `<%= e.singular %>` by id. Returns `nil` if not found or not visible."
def <%= e.get_fn %>(id, context \\ %{}) do
<%= e.module_short %>
|> scope_tenant(context)
|> apply_read_permission(<%= inspect e.entity_name %>, context)
|> Repo.get(id)
end
@doc "Fetch a single `<%= e.singular %>` by id. Raises if not found or not visible."
def <%= e.get_bang_fn %>(id, context \\ %{}) do
<%= e.module_short %>
|> scope_tenant(context)
|> apply_read_permission(<%= inspect e.entity_name %>, context)
|> Repo.get!(id)
end
@doc "Build an unsaved `<%= e.singular %>` changeset, for use in forms."
def <%= e.change_fn %>(%<%= e.module_short %>{} = <%= e.singular %>, attrs \\ %{}) do
<%= e.module_short %>.changeset(<%= e.singular %>, attrs)
end
@doc "Create a `<%= e.singular %>`: authorize, apply on_create hook, insert."
def <%= e.create_fn %>(attrs, context \\ %{}) do
with :ok <- authorize_create(<%= inspect e.entity_name %>, context) do
%<%= e.module_short %>{}
|> <%= e.module_short %>.changeset(attrs)
|> inject_tenant_id(context)
|> apply_changeset_hook(:on_create, <%= inspect e.entity_name %>, context)
|> Repo.insert()
end
end
@doc "Update a `<%= e.singular %>`: authorize, apply on_update hook, update."
def <%= e.update_fn %>(%<%= e.module_short %>{} = <%= e.singular %>, attrs, context \\ %{}) do
with :ok <- authorize_update(<%= inspect e.entity_name %>, <%= e.singular %>, context) do
<%= e.singular %>
|> <%= e.module_short %>.changeset(attrs)
|> apply_changeset_hook(:on_update, <%= inspect e.entity_name %>, context)
|> Repo.update()
end
end
@doc "Delete a `<%= e.singular %>`: authorize, run on_delete hook, delete."
def <%= e.delete_fn %>(%<%= e.module_short %>{} = <%= e.singular %>, context \\ %{}) do
with :ok <- authorize_delete(<%= inspect e.entity_name %>, <%= e.singular %>, context),
:ok <- run_delete_hook(<%= inspect e.entity_name %>, <%= e.singular %>, context) do
Repo.delete(<%= e.singular %>)
end
end
<% end %>
# --- Internal: permission + hook dispatch ---------------------------------
defp apply_read_permission(query, entity, context) do
<%= inspect @domain_module %>.__caravela_permission__(:can_read, entity, query, context)
end
defp authorize_create(entity, context) do
case <%= inspect @domain_module %>.__caravela_permission__(:can_create, entity, context) do
true -> :ok
false -> {:error, :unauthorized}
end
end
defp authorize_update(entity, struct, context) do
case <%= inspect @domain_module %>.__caravela_permission__(:can_update, entity, struct, context) do
true -> :ok
false -> {:error, :unauthorized}
end
end
defp authorize_delete(entity, struct, context) do
case <%= inspect @domain_module %>.__caravela_permission__(:can_delete, entity, struct, context) do
true -> :ok
false -> {:error, :unauthorized}
end
end
defp apply_changeset_hook(changeset, action, entity, context) do
<%= inspect @domain_module %>.__caravela_hook__(action, entity, changeset, context)
end
defp run_delete_hook(entity, struct, context) do
case <%= inspect @domain_module %>.__caravela_hook__(:on_delete, entity, struct, context) do
:ok -> :ok
{:error, _} = err -> err
other -> raise "on_delete hook must return :ok or {:error, reason}, got: " <> inspect(other)
end
end
# --- Internal: multi-tenant scoping --------------------------------------
<%= if @multi_tenant do %>
defp scope_tenant(query, %{tenant: %{id: tenant_id}}) when not is_nil(tenant_id) do
where(query, [q], q.tenant_id == ^tenant_id)
end
defp scope_tenant(query, _context), do: query
defp inject_tenant_id(changeset, %{tenant: %{id: tenant_id}}) when not is_nil(tenant_id) do
Ecto.Changeset.put_change(changeset, :tenant_id, tenant_id)
end
defp inject_tenant_id(changeset, _context), do: changeset
<% else %>
defp scope_tenant(query, _context), do: query
defp inject_tenant_id(changeset, _context), do: changeset
<% end %>
<%= @custom_marker %>