# `MishkaCmsMigration.Source.Item`
[🔗](https://github.com/mishka-group/mishka_cms_migration/blob/v0.0.1/lib/mishka_cms_migration/source.ex#L448)

One item of a source, as every source hands it over: the normalized model the dry run decides on
and a target writes from. A source's `c:MishkaCmsMigration.Source.normalize/3` returns the map
`build/1` turns into this struct; the walk puts `:kind` in, so a source never names it.

It lives with the source behaviour because it is the source's answer, held only while a job
runs. What is kept of it afterwards is `MishkaCmsMigration.MigrationItem`: the CMS record it was
written to, its title, its `extra`, its hash and what the last run did with it, not its content.

  * `:kind` — what the item is, such as `"post"`, `"page"` or `"user"`. A target is chosen by it
    (`MishkaCmsMigration.Target`).
  * `:source_id` — the item's id on the source, kept as a string (an integer is turned into one).
    Together with the run's `source_host` and `:kind` it is how a re-run finds what an earlier run
    wrote.
  * `:title` — what the report calls the item.
  * `:parent` — the source id of the item of the same kind this one sits under, such as a
    category's parent category, kept as a string; `nil` for none. The walk writes the parent
    first, whichever the source lists first, and the target is handed the CMS record it was
    written to (`MishkaCmsMigration.Target`'s context, `parent_id`).
  * `:modified_at` — when the source last changed it: a `DateTime`, a `NaiveDateTime` taken as
    UTC, or an ISO 8601 string.
  * `:attrs` — everything else the target needs, as the source names it. Its keys are never
    turned into atoms.
  * `:extra` — every field of the row the CMS has no home for (a WordPress post's custom fields,
    ACF fields, another plugin's meta), as key and value. It is kept on the item's
    `MishkaCmsMigration.MigrationItem` (its `extra`), and a target whose resource has an `extra`
    of its own may copy it there (`MishkaCmsMigration.Target`). Its keys are never turned into
    atoms.
  * `:content` — the item's body as a list of pieces, in order. A piece the source converted
    itself carries `:html`; a block or shortcode it could not convert carries `:name`, `:attrs`
    and `:raw` instead, and is offered to the `migration:content:convert` event
    (`MishkaCmsMigration.Events`) when the item is imported. `html/1` joins the pieces.

`hash/1` is what a re-run compares to tell whether the source changed the item: it covers every
field but `:modified_at`, so a source that touches an item without changing it is not an update.

# `t`

```elixir
@type t() :: %MishkaCmsMigration.Source.Item{
  attrs: map() | nil,
  content: list() | nil,
  extra: map() | nil,
  kind: String.t(),
  modified_at: DateTime.t() | nil,
  parent: String.t() | nil,
  source_id: String.t(),
  title: String.t() | nil
}
```

# `build`

```elixir
@spec build(map()) :: {:ok, t()} | {:error, [map()]}
```

The item `attrs` describe, checked and cleaned: `{:ok, item}`, or `{:error, errors}` with
guarded_struct's error maps.

# `builder`

# `enforce_keys`

# `enforce_keys`

# `example`

A sample %MishkaCmsMigration.Source.Item{} populated from defaults + type-based fallbacks.

# `hash`

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

A digest of what the source says the item is: every field but `:modified_at`, as hex.

# `html`

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

The item's content as one string: the `:html` of each piece, in order. A piece with no `:html`
adds nothing, so convert the pieces first.

# `keys`

# `keys`

---

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