# `MishkaBlog.Resources.Changes.RestoreVersion`
[🔗](https://github.com/mishka-group/mishka_blog/blob/v0.0.1/lib/mishka_blog/resources/__changes/restore_version.ex#L5)

Restores a record to one of its own versions, named by the action's `:version_id` argument.

A version of any other record, of this site or another, is refused with `refusal/1`, and a
version that does not exist with a not-found error. Only the attributes in
`:fields` are copied back, and never `site_id`, so a restore cannot move a record to another site.
The check holds for a call made with `authorize?: false` too.

## Usage

    update :restore_version do
      accept []
      argument :version_id, :uuid, allow_nil?: false

      change {MishkaBlog.Resources.Changes.RestoreVersion,
              version: MishkaBlog.Tag.Version,
              fields: [:name, :slug, :description],
              label: "tag"}
    end

## Options

* `:version` - Required. The record's version resource. It needs a `:get_any` read.
* `:fields` - Required. The attributes a restore copies back.
* `:label` - Required. How the refusal names the record, such as "tag".

# `cast_field`

```elixir
@spec cast_field(%{required(atom()) =&gt; term()}, Ash.Resource.t(), atom()) :: %{
  required(atom()) =&gt; term()
}
```

`changes` with the string value of `field` read through the attribute's own type on `resource`:
`"active"` becomes `:active` for an atom attribute. A value the type refuses is dropped, so the
record keeps its current one; a field that is absent or not a string is left as it is.

# `known_changes`

```elixir
@spec known_changes(map() | String.t() | nil, [atom()]) :: %{
  required(atom()) =&gt; term()
}
```

The entries of a version's `changes` whose key names one of `fields`, keyed by that atom. A key
that names no field is left out, so reading a version never creates an atom:

    known_changes(%{"name" => "Elixir", "junk" => 1}, [:name, :slug]) #=> %{name: "Elixir"}

`changes` may be a map with string or atom keys, or its JSON; anything else, JSON that does not
parse or is not an object included, reads as `%{}`.

# `preview`

```elixir
@spec preview([struct()], keyword(), String.t(), keyword()) ::
  {:ok, [struct()]} | {:error, Exception.t()}
```

Each of `records` as it was in the version `version_id`, read with `read_opts` (the caller's
actor, tenant and `authorize?`) through the version's `:get_any` read:

    preview(results, [version: MishkaBlog.Tag.Version, label: "tag"], version_id,
            Ash.Scope.to_opts(context))

`opts` are the options of this change, `:version` and `:label`, and two more:

* `:fields` - The attributes copied from the version. Defaults to every field of the record.
* `:cast` - Fields whose string value is read through the attribute's own type, as `cast_field/3`
  does. Defaults to none.

A version that belongs to another record is refused with `refusal/1` of `:label`, and one that
cannot be read comes back as the read's own error. Neither raises.

# `refusal`

```elixir
@spec refusal(String.t()) :: String.t()
```

The refusal of a version that is another record's: "Version does not belong to this {label}.",
translated in the `errors` domain of `MishkaCmsCore.Gettext` in the locale of the calling process,
`label` translated there too. A preview of another record's version is refused with it as well.

---

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