# `Arc.Filter`
[🔗](https://github.com/Cratis/Arc.Elixir/blob/main/lib/arc/filter.ex#L4)

A cross-cutting rule that runs for commands and queries, whatever their own declarations say.

`authorize` protects one artifact. A filter protects many: a whole namespace, or everything that
matches a rule. Register filters on the application; they run in order, after the artifact's own
authorization and before validation or the query:

    config :my_app, MyApp.Arc, filters: [MyApp.SecuredNamespace]

    defmodule MyApp.SecuredNamespace do
      @behaviour Arc.Filter

      @impl true
      def on_command(ctx, %Arc.Descriptor{namespace: ["MyApp", "Secured" | _]}),
        do: require_role(ctx)

      def on_command(_ctx, _descriptor), do: :ok

      @impl true
      def on_query(ctx, %Arc.Descriptor{namespace: ["MyApp", "Secured" | _]}, _query),
        do: require_role(ctx)

      def on_query(_ctx, _descriptor, _query), do: :ok

      defp require_role(ctx) do
        if "Secured" in ctx.principal.roles,
          do: :ok,
          else: {:unauthorized, "Role 'Secured' is required."}
      end
    end

Both callbacks are optional. A filter answers

  * `:ok` - carry on
  * `{:unauthorized, reason}` - the caller may not: the result is not authorized (HTTP 403) and
    carries the reason. For an observable query the subscription ends with `Unauthorized`
  * `{:error, results}` - reject with validation results

A filter sees the *descriptor* of the artifact (`Arc.Descriptor`, with `module`, `name` and
`namespace`) and the context, not the payload.

# `outcome`

```elixir
@type outcome() :: :ok | {:unauthorized, String.t()} | {:error, term()}
```

# `on_command`
*optional* 

```elixir
@callback on_command(Arc.Context.t(), Arc.Descriptor.t()) :: outcome()
```

# `on_query`
*optional* 

```elixir
@callback on_query(Arc.Context.t(), Arc.Descriptor.t(), Arc.Descriptor.Query.t()) ::
  outcome()
```

---

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