Ash DataLayer for Neo4j.
Errors (#372)
Every error the behaviour callbacks (create/update/destroy/run_query/…)
return is a typed struct — never a bare string (a string becomes
Ash.Error.Unknown.UnknownError: unclassified and only substring-matchable).
Pick by meaning:
- Server/Bolt error →
AshNeo4j.Error.Neo4j(classifies by category). - Deliberate refusal ("can't push this down / won't render it") → the
AshNeo4j.Error.Unsupported*/Requires*family (class: :invalid). - The node/edge we expected to act on wasn't there → reuse Ash's own:
Ash.Error.Changes.StaleRecord(the record is gone / a guard no longer holds) orAsh.Error.Invalid.Unavailable(a preservation guard blocked a destroy). - An internal invariant we couldn't satisfy (unexpected input shape, a
relationship/aggregate path that won't resolve) →
AshNeo4j.Error.Internal(class: :unknown).
Two standing rules: reuse an Ash error before inventing an AshNeo4j.Error.*,
and return, never raise, from the behaviour path. Internal sentinels between
helpers (e.g. {:error, :nothing_deleted} from run_expecting_deletions/2) are
atoms, not strings, and are converted to a typed error before escaping. The
contract is enforced by test/error_contract_test.exs.
Ash.Type callbacks (cast/dump/load) are a separate contract — they follow Ash's
type-error convention (message strings), not this one.
neo4j
Examples
neo4j do
label :Comment
relate [{:post, :BELONGS_TO, :outgoing, :Post}]
end
Options
| Name | Type | Default | Docs |
|---|---|---|---|
label | atom | Optional node label | |
relate | list({atom, atom, atom, atom}) | [] | Optional list of relationships, as tuples of {relationship_name, edge_label, edge_direction, destination_label} |
guard | list({atom, atom, atom}) | [] | Optional list of node relationships, as tuples of {edge_label, edge_direction, destination_label} |
skip | list(atom) | [] | Optional list of attributes not to be stored directly as node properties |