AshPaperPlane

An extension to AshPaperTrail, which is itself an extension to Ash. It augments the version resource AshPaperTrail generates, and does two independent things.

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

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