# `AshScim.Patch`
[🔗](https://github.com/ash-project/ash_scim/blob/main/lib/ash_scim/patch.ex#L5)

Translates a SCIM 2.0 PATCH request body (RFC 7644 §3.5.2) into a
structured input describing both attribute updates and relationship-level
operations:

    %{
      attrs: %{first_name: "Alicia"},
      relationships: [
        {:append, :memberships, [%{user_id: "u1"}]},
        {:remove_where, :memberships, %{user_id: %{eq: "u2"}}}
      ]
    }

The router applies `attrs` via the resource's update action and each
relationship op as either a `manage_relationship/4` call or a custom
load+destroy/update step.

## Supported operations

  * `add` / `replace` with no path — value is treated as a partial SCIM
    resource and decoded via `AshScim.Decoder`. Only attribute changes are
    applied (relationship arrays in path-less ops are ignored, since the
    semantics of "add a partial resource that includes a member list"
    aren't well-defined).
  * `add` / `replace` / `remove` with a simple path (e.g. `active`,
    `name.givenName`) — maps to the corresponding Ash attribute.
  * `add path: "emails" value: [...]` — append-style relationship op.
  * `replace path: "emails" value: [...]` — full-replace relationship op.
  * `remove path: "emails[value eq "x"]"` — load-and-destroy op against
    the related resource matching the bracket filter.

When the multivalued declares `mirror_primary_to:`, the patch result also
carries a `:mirror_sync` post-op (or a synchronously-computed attr write
for replace) that keeps the parent's mirrored scalar in sync with the
primary entry's value.

# `op`

```elixir
@type op() :: :add | :remove | :replace
```

# `rel_op`

```elixir
@type rel_op() ::
  {:append, atom(), [%{required(atom()) =&gt; term()}]}
  | {:replace_all, atom(), [%{required(atom()) =&gt; term()}]}
  | {:remove_where, atom(), %{required(atom()) =&gt; term()}}
  | {:mirror_sync, atom(), atom(), atom()}
```

# `result`

```elixir
@type result() :: %{attrs: %{required(atom()) =&gt; term()}, relationships: [rel_op()]}
```

# `schema`

Public schema URN for the PatchOp message.

# `to_params`

```elixir
@spec to_params(%{required(String.t()) =&gt; term()}, module()) ::
  {:ok, result()} | {:error, term()}
```

Build the structured patch input from a PATCH body. Returns
`{:ok, %{attrs: …, relationships: …}}` or `{:error, reason}`.

---

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