Packages
ash_paper_plane
0.1.0
AshPaperTrail extension that lifts action context onto a version's metadata and enqueues an Oban job when the version is created.
Current section
Files
Jump to
Current section
Files
ash_paper_plane
README.md
README.md
# AshPaperPlane
An extension to [AshPaperTrail](https://hexdocs.pm/ash_paper_trail), which is
itself an extension to [Ash](https://hexdocs.pm/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.
```elixir
def deps do
[
{:ash_paper_plane, "~> 0.1.0"}
]
end
```
The API reference is at [hexdocs.pm/ash_paper_plane](https://hexdocs.pm/ash_paper_plane).
## Usage
Both halves arrive through the `paper_trail` block's `mixin` option.
```elixir
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
```