# `TerminusDB.Diff`
[🔗](https://github.com/thanos/terminusdb-client-elixir/blob/v0.3.3/lib/terminus_db/diff.ex#L1)

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 (`before` and `after` maps).
- Branch vs branch, commit vs commit, or branch vs commit (by supplying the
  appropriate resource refs in the `before`/`after` fields).

## 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"
    )

# `apply_opt`

```elixir
@type apply_opt() ::
  {:before_version, String.t()}
  | {:after_version, String.t()}
  | {:message, String.t()}
  | {:author, String.t()}
  | {:organization, String.t()}
  | {:repo, String.t()}
```

# `compare_opt`

```elixir
@type compare_opt() ::
  {:before, map()}
  | {:after, map()}
  | {:keep, map()}
  | {:organization, String.t()}
```

# `diff_object_opt`

```elixir
@type diff_object_opt() ::
  {:before, map()}
  | {:after, map()}
  | {:keep, map()}
  | {:organization, String.t()}
  | {:repo, String.t()}
```

# `diff_version_opt`

```elixir
@type diff_version_opt() ::
  {:before_version, String.t()}
  | {:after_version, String.t()}
  | {:organization, String.t()}
  | {:repo, String.t()}
```

# `patch_opt`

```elixir
@type patch_opt() :: {:organization, String.t()}
```

# `patch_resource_opt`

```elixir
@type patch_resource_opt() ::
  {:patch, map()}
  | {:message, String.t()}
  | {:author, String.t()}
  | {:match_final_state, boolean()}
  | {:organization, String.t()}
  | {:repo, String.t()}
```

# `apply`

```elixir
@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` - overrides `config.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"

# `apply!`

```elixir
@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"}

# `compare`

```elixir
@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 `@id` and 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` - overrides `config.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

# `compare!`

```elixir
@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"}}

# `diff_object`

```elixir
@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` - overrides `config.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"

# `diff_object!`

```elixir
@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"

# `diff_version`

```elixir
@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` - overrides `config.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
    %{}

# `diff_version!`

```elixir
@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
    %{}

# `patch`

```elixir
@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` - overrides `config.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"

# `patch!`

```elixir
@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"}

# `patch_resource`

```elixir
@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` - overrides `config.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"

# `patch_resource!`

```elixir
@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"}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
