Journal tracks changes (i.e. Insert, Updates, Deletes) on Ecto Schemas. This can be useful for a variety of cases, such as for keeping an activity log or for auditing changes to tables over time.
Note: this library is still very early-stage, use at your own risk.
Note: this library has only been tested with Postgres.
Setup
Make sure to have both ecto and ecto_sql in your dependencies,
then add {journal: "0.1.0"}
First, you need to run an initialization script that will create a migration for a new table and a stored procedure with the following mix task:
Now you're ready to add write-tracking to your existing Ecto.Schemas one-by-one by running this mix task:
$ mix journal.gen schema_name
Finally, commit your changes by running the migrations:
Once you've successfully migrated, drop the use Journal macro into your model
to get access to journal of this model:
defmodule Todos.Todo do use Journal schema "todos" do field :title, :string field :status, :string end end
Usage
To get a todos history, you can now call Todos.Todo.history_of(id) to get the query needed
to list the changes. You can still append to this query, e.g. for filtering or sorting purposes.
For example:
{:ok, todo} = Journal.Repo.insert(%Journal.Todo{title: "Foo", status: "open"}) Journal.Todo.history_of(todo.id) |> Journal.Repo.all() [ %Journal.Todo.History{ __meta__: #Ecto.Schema.Metadata<:loaded, "journal_journal">, id: 1, inserted_at: ~U[2019-12-03 17:21:04.639808Z], new_val: %Journal.Todo{ __meta__: #Ecto.Schema.Metadata<:loaded, "todos">, id: 1, status: "open", title: "Foo" }, old_val: nil, operation: "INSERT", table_name: "todos" } ] Journal.Repo.update(cast(todo, %{title: "Bar"}, [:title])) Journal.Todo.history_of(todo.id) |> Journal.Repo.all() [ %Journal.Todo.History{ __meta__: #Ecto.Schema.Metadata<:loaded, "journal_journal">, id: 2, inserted_at: ~U[2019-12-03 17:21:04.643228Z], new_val: %Journal.Todo{ __meta__: #Ecto.Schema.Metadata<:loaded, "todos">, id: 1, status: "open", title: "Bar" }, old_val: %Journal.Todo{ __meta__: #Ecto.Schema.Metadata<:loaded, "todos">, id: 1, status: "open", title: "Foo" }, operation: "UPDATE", table_name: "todos" }, %Journal.Todo.History{ __meta__: #Ecto.Schema.Metadata<:loaded, "journal_journal">, id: 1, inserted_at: ~U[2019-12-03 17:21:04.639808Z], new_val: %Journal.Todo{ __meta__: #Ecto.Schema.Metadata<:loaded, "todos">, id: 1, status: "open", title: "Foo" }, old_val: nil, operation: "INSERT", table_name: "todos" } ]
More
This feature leverages a postgres trigger as described here: https://www.cybertec-postgresql.com/en/tracking-changes-in-postgresql/