After doing the Ash Livebook Getting started for several times over the last 2? years,
I finally have a small project that I dare to tackle with Ash.
Loving it so far.
However, sometimes I am not quite sure what to do when receiving an error.
Basic Idea / Context
- The system is meant to collect “Proposals”.
- Proposals can be handed in, updated, accepted and rejected during a given timespan.
- Each given timespan is called a Proposal Round.
- All Proposals are related to a Proposal Round. They can only be interacted with while the Proposal Round is active.
- The Proposal Round being active is checked in a custom validation
The custom validation
defmodule Validations.OpenForProposals do
use Ash.Resource.Validation
@impl true
# is this how we would properly check the round_id?
def validate(%{attributes: %{round_id: id}}, _opts, _context) when not is_nil(id) do
validate_open(id)
end
def validate(%{data: %{round_id: id}}, _opts, _context) when not is_nil(id) do
validate_open(id)
end
def validate(_changeset, _opts, _context) do
{:error, field: :round_id, message: "No proposal round was set."}
end
def validate_open(round_id) do
with {:ok, round} <- Ash.get(Proposal, round_id),
true <- round.is_open == true do
IO.puts("OPEN FOR PROPOSALS") # ---- THIS IS PRINTED :) --------
:ok
else
false -> {:error, field: :round_id, message: "Not currently open for proposals."}
e -> e
end
end
end
Domain Module
defmodule Proposals do
use Ash.Domain
resources do
resource Proposal do
define :propose,
args: [:employee_id, :round_id, :text],
action: :propose
define :accept,
args: [:comment],
action: :accept
define :reject,
args: [:comment],
action: :reject
end
end
end
Resource Module
defmodule Proposal do
use Ash.Resource, domain: Proposals, data_layer: AshPostgres.DataLayer
actions do
defaults [:read, :destroy, update: :*]
create :propose do
accept [:employee_id, :round_id, :text]
validate {Validations.OpenForProposals, []}
end
update :accept do
accept [:comment]
validate {Validations.OpenForProposals, []}
change set_attribute(:status, :accepted)
change set_attribute(:comment, arg(:comment), set_when_nil?: false)
end
update :reject do
accept [:comment]
validate {Validations.OpenForProposals, []}
change set_attribute(:status, :rejected)
change set_attribute(:comment, arg(:comment), set_when_nil?: false)
end
end
attributes do
# ...
end
relationships do
# ...
end
postgres do
# ..
end
end
The error
The current error is the following:
iex> {:ok, proposal} = Proposals.propose(me.id, 1, "Add 5 more chairs to the break room.")
{:ok,
#Proposal<
...
>}
iex > Proposals.accept(proposal, "Good idea. Matthew will take care of it.")
%Ash.Error.framework{
errors: [
%Ash.Error.Framework.MustBeAtomic{
resource: Proposal,
action: :accept,
reason: "Validations.OpenForProposals does not implement `atomic/3`",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :framework
}]}
So the validation works on :propose, but not on :accept.
I have read Basic bulk actions, atomics, new stream options, `error/2` expression and Update Actions — ash v3.0.9, but I do not understand what to do or what the error actually means (and why it occurs on the validation?!)
What am I not seeing / understanding and where I could I learn about it?