Document diff and patch API for TerminusDB.
Wraps the /api/diff, /api/patch, and /api/apply endpoints to compare,
patch, and apply document changes.
Diffs can be computed between:
- Two document values (
beforeandaftermaps). - Branch vs branch, commit vs commit, or branch vs commit (by supplying the
appropriate resource refs in the
before/afterfields).
Quick start
config =
TerminusDB.Config.new(endpoint: "http://localhost:6363")
|> TerminusDB.Config.with_database("mydb")
# Diff two document values
{:ok, patch} = TerminusDB.Diff.diff_object(config,
before: %{"@id" => "Person/Alice", "name" => "Alice"},
after: %{"@id" => "Person/Alice", "name" => "Alicia"}
)
# Apply a patch to a branch
{:ok, _} = TerminusDB.Diff.patch_resource(config,
patch: patch, message: "update name", author: "admin"
)
Summary
Functions
Diffs two commits and applies the changes onto a branch.
Applies a diff to a branch, or raises.
Compares two document states and returns a structured diff patch.
Compares two document states, or raises TerminusDB.Error.
Diffs two concrete document objects and returns a TerminusDB.Patch struct.
Diffs two concrete document objects, or raises.
Diffs two commit/branch versions and returns a TerminusDB.Patch struct.
Diffs two versions, or raises.
Applies a patch to a "before" object and returns the "after" object (no commit).
Applies a patch, or raises.
Applies a patch to a branch resource (commits the change).
Applies a patch to a branch resource, or raises.
Types
Functions
@spec apply(TerminusDB.Config.t(), [apply_opt()]) :: {:ok, map()} | {:error, TerminusDB.Error.t()}
Diffs two commits and applies the changes onto a branch.
Options
:before_version(required) - the before commit descriptor.:after_version(required) - the after commit descriptor.:message- commit message.:author- commit author.:organization- overridesconfig.organization.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"api:status" => "api:success"})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, resp} = TerminusDB.Diff.apply(config,
...> before_version: "admin/mydb/local/commit/abc",
...> after_version: "admin/mydb/local/commit/def",
...> author: "admin", message: "apply"
...> )
iex> resp["api:status"]
"api:success"
@spec apply!(TerminusDB.Config.t(), [apply_opt()]) :: map()
Applies a diff to a branch, or raises.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"api:status" => "api:success"})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> TerminusDB.Diff.apply!(config,
...> before_version: "admin/mydb/local/commit/abc",
...> after_version: "admin/mydb/local/commit/def"
...> )
%{"api:status" => "api:success"}
@spec compare(TerminusDB.Config.t(), [compare_opt()]) :: {:ok, map()} | {:error, TerminusDB.Error.t()}
Compares two document states and returns a structured diff patch.
The before and after values can be:
- Document maps (with
@idand fields) for a value-level diff. - Resource references (e.g.
"admin/mydb/local/branch/main") for a branch/commit-level diff.
Options
:before(required) - the "before" document or resource ref.:after(required) - the "after" document or resource ref.:keep- a map of fields to preserve in the diff (e.g.%{"@id" => true}).:organization- overridesconfig.organization.
Examples
Diff two document values:
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req ->
...> {req, Req.Response.new(status: 200, body: %{"name" => %{"@op" => "ValueSwap", "@before" => "Alice", "@after" => "Alicia"}})}
...> end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, patch} = TerminusDB.Diff.compare(config,
...> before: %{"@id" => "Person/Alice", "name" => "Alice"},
...> after: %{"@id" => "Person/Alice", "name" => "Alicia"}
...> )
iex> patch["name"]["@op"]
"ValueSwap"Diff two branches:
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, _} = TerminusDB.Diff.compare(config,
...> before: "admin/mydb/local/branch/main",
...> after: "admin/mydb/local/branch/feature"
...> )
:ok
@spec compare!(TerminusDB.Config.t(), [compare_opt()]) :: map()
Compares two document states, or raises TerminusDB.Error.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"name" => %{"@op" => "ValueSwap"}})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> TerminusDB.Diff.compare!(config,
...> before: %{"name" => "Alice"},
...> after: %{"name" => "Alicia"}
...> )
%{"name" => %{"@op" => "ValueSwap"}}
@spec diff_object(TerminusDB.Config.t(), [diff_object_opt()]) :: {:ok, TerminusDB.Patch.t()} | {:error, TerminusDB.Error.t()}
Diffs two concrete document objects and returns a TerminusDB.Patch struct.
Options
:before(required) - the "before" document map.:after(required) - the "after" document map.:keep- a map of fields to preserve in the diff.:organization- overridesconfig.organization.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req ->
...> {req, Req.Response.new(status: 200, body: %{"name" => %{"@op" => "SwapValue", "@before" => "old", "@after" => "new"}})}
...> end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, patch} = TerminusDB.Diff.diff_object(config,
...> before: %{"@id" => "Person/1", "name" => "old"},
...> after: %{"@id" => "Person/1", "name" => "new"}
...> )
iex> patch.content["name"]["@after"]
"new"
@spec diff_object!(TerminusDB.Config.t(), [diff_object_opt()]) :: TerminusDB.Patch.t()
Diffs two concrete document objects, or raises.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req ->
...> {req, Req.Response.new(status: 200, body: %{"name" => %{"@op" => "SwapValue", "@before" => "old", "@after" => "new"}})}
...> end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> patch = TerminusDB.Diff.diff_object!(config,
...> before: %{"name" => "old"},
...> after: %{"name" => "new"}
...> )
iex> patch.content["name"]["@after"]
"new"
@spec diff_version(TerminusDB.Config.t(), [diff_version_opt()]) :: {:ok, TerminusDB.Patch.t()} | {:error, TerminusDB.Error.t()}
Diffs two commit/branch versions and returns a TerminusDB.Patch struct.
Options
:before_version(required) - the before commit/branch descriptor.:after_version(required) - the after commit/branch descriptor.:organization- overridesconfig.organization.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, patch} = TerminusDB.Diff.diff_version(config,
...> before_version: "admin/mydb/local/branch/main",
...> after_version: "admin/mydb/local/branch/feature"
...> )
iex> patch.content
%{}
@spec diff_version!(TerminusDB.Config.t(), [diff_version_opt()]) :: TerminusDB.Patch.t()
Diffs two versions, or raises.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> patch = TerminusDB.Diff.diff_version!(config,
...> before_version: "admin/mydb/local/branch/main",
...> after_version: "admin/mydb/local/branch/feature"
...> )
iex> patch.content
%{}
@spec patch(TerminusDB.Config.t(), [patch_opt() | {:before, map()} | {:patch, map()}]) :: {:ok, map()} | {:error, TerminusDB.Error.t()}
Applies a patch to a "before" object and returns the "after" object (no commit).
Options
:organization- overridesconfig.organization.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"@id" => "Person/1", "name" => "new"})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, after_obj} = TerminusDB.Diff.patch(config,
...> before: %{"@id" => "Person/1", "name" => "old"},
...> patch: %{"name" => %{"@op" => "SwapValue", "@before" => "old", "@after" => "new"}}
...> )
iex> after_obj["name"]
"new"
@spec patch!(TerminusDB.Config.t(), [patch_opt() | {:before, map()} | {:patch, map()}]) :: map()
Applies a patch, or raises.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"name" => "new"})} end
...> )
iex> TerminusDB.Diff.patch!(config,
...> before: %{"name" => "old"},
...> patch: %{"name" => %{"@op" => "SwapValue", "@after" => "new"}}
...> )
%{"name" => "new"}
@spec patch_resource(TerminusDB.Config.t(), [patch_resource_opt()]) :: {:ok, map()} | {:error, TerminusDB.Error.t()}
Applies a patch to a branch resource (commits the change).
Options
:patch(required) - the patch content.:message- commit message.:author- commit author.:match_final_state- boolean.:organization- overridesconfig.organization.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"api:status" => "api:success"})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> {:ok, resp} = TerminusDB.Diff.patch_resource(config,
...> patch: %{"name" => %{"@op" => "SwapValue", "@after" => "new"}},
...> author: "admin", message: "update"
...> )
iex> resp["api:status"]
"api:success"
@spec patch_resource!(TerminusDB.Config.t(), [patch_resource_opt()]) :: map()
Applies a patch to a branch resource, or raises.
Examples
iex> config = TerminusDB.Config.new(
...> endpoint: "http://localhost:6363",
...> adapter: fn req -> {req, Req.Response.new(status: 200, body: %{"api:status" => "api:success"})} end
...> ) |> TerminusDB.Config.with_database("mydb")
iex> TerminusDB.Diff.patch_resource!(config, patch: %{})
%{"api:status" => "api:success"}