AshPaperPlane
An extension to AshPaperTrail, which is itself an extension to Ash. It augments the version resource AshPaperTrail generates, and does two independent things.
- It lifts the action's shared context onto the version, so a version records where a change came from rather than only what changed.
- It enqueues an Oban job when a version is created, in the data layer's transaction where the data layer opens one.
A resource asks for either or both.
Installation
This library is experimental and under active development. Its API, code, and design are subject to change.
def deps do
[
{:ash_paper_plane, "~> 0.1.0"}
]
end
The API reference is at hexdocs.pm/ash_paper_plane.
Usage
Both halves arrive through the paper_trail block's mixin option.
defmodule MyApp.Blog.Post do
use Ash.Resource,
domain: MyApp.Blog,
extensions: [AshPaperTrail.Resource]
paper_trail do
mixin {AshPaperPlane.VersionMixin, :extend,
[[metadata: true, worker: MyApp.VersionCreatedWorker]]}
end
end
metadata: true adds a metadata attribute to the version and fills it. A
worker enqueues a job. Neither implies the other, so a resource wanting one
names one.
Six options shape the two halves, and AshPaperPlane.VersionMixin documents
each with its default.
The metadata half carries what you stamp
The keys come from source_context.shared, which is the application's to fill.
A key absent from the context, or carried with a nil value, is left out.
Stamping it is the caller's job, and the natural place is wherever a verified value already exists:
post
|> Ash.Changeset.for_update(:update, params)
|> Ash.Changeset.set_context(%{shared: %{origin: "web", correlation_id: id}})
|> Ash.update!()
That version records origin and correlation_id because they were set, and
omits causation_id because it was not.
The Oban half rides your transaction, if you have one
The job is inserted in an after_action hook. Where the resource's data layer
opens a transaction on the repo Oban is configured with, the insert joins it. A
job cannot then survive an action that rolled back, and a version cannot commit
while its job is lost.
Both conditions are yours to meet rather than the library's to guarantee. A resource whose data layer opens no transaction, or an Oban pointed at another repo, still enqueues its job, with nothing tying the two together.
What it leaves to you
- Running the worker. What the job does when it runs is yours.
- Deciding what belongs in
shared. The mixin carries the keys you name. - The version resource itself, which is AshPaperTrail's.
Testing this library
The suite needs PostgreSQL, because Oban's assertions read its jobs table. Point
it at one with POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, and
POSTGRES_DB, or take the defaults, and run:
mix test