Runtime resolution for the generated undo action: decides whether a record
may be rewound, and to where.
Undo reverses the most recent state change in the transition log. It is
refused unless that change came from a transition declared undoable?: true — which is what keeps automatic steps, timeouts, error paths and the
initial row out of reach, since none of them corresponds to an undoable
transition. See AshWorkflow.Errors.UndoNotPermitted for the full set of
refusals.
Nothing here writes. The decision is separated from the write so it can be
asked without side effects — can_undo?/2 and undo_target/2 are the same
code path the action itself takes.
Summary
Types
The resolved undo: the state to rewind to, and the log row being reversed.
Functions
Resolves the undo for record, or returns why it is refused.
Returns the state record would rewind to, or nil if it cannot be undone.
Returns true if record can be undone by actor.
Types
@type resolution() :: %{target: atom(), row: Ash.Resource.record()}
The resolved undo: the state to rewind to, and the log row being reversed.
Functions
@spec resolve(Ash.Resource.record(), term()) :: {:ok, resolution()} | {:error, Exception.t()}
Resolves the undo for record, or returns why it is refused.
@spec undo_target(Ash.Resource.record(), term()) :: atom() | nil
Returns the state record would rewind to, or nil if it cannot be undone.
@spec undoable?(Ash.Resource.record(), term()) :: boolean()
Returns true if record can be undone by actor.
Named undoable? rather than can_undo? because Ash's code interface
already generates can_undo?/2 for the undo action, and that answers a
different question — whether the actor is authorized to call it, not
whether there is anything to undo.