# `Tayori.Resolver`
[🔗](https://github.com/hattoriai/tayori/blob/v0.1.0/lib/tayori/resolver.ex#L1)

How Tayori knows the people of the app: who may write the news in the
admin, and who reads it. The app writes one module, from what it
already has (its users, their plans and roles, its admin session), and
gives it to the router macros (`Tayori.Router`) and to `Tayori.Live`:

    defmodule MyAppWeb.TayoriResolver do
      @behaviour Tayori.Resolver

      # The admin.

      @impl true
      def resolve_user(conn), do: conn.assigns[:current_admin]

      @impl true
      def resolve_access(%MyApp.Admin{}), do: :all
      def resolve_access(_nobody), do: {:forbidden, "/admin/login"}

      @impl true
      def author(admin), do: %{"id" => admin.id, "name" => admin.name}

      @impl true
      def segments do
        [
          {"Plan", [{"plan:free", "Free"}, {"plan:pro", "Pro"}]},
          {"Role", [{"role:owner", "Owners"}, {"role:editor", "Editors"}]}
        ]
      end

      # The readers.

      @impl true
      def resolve_reader(%{assigns: %{current_scope: %{user: user}}}) when not is_nil(user) do
        %Tayori.Reader{
          id: "user:#{user.id}",
          segments: ["plan:#{user.plan}", "role:#{user.role}"],
          locale: user.locale,
          since: user.inserted_at
        }
      end

      def resolve_reader(_visitor), do: nil
    end

## The admin

`c:resolve_user/1` reads the admin from the request (the `Plug.Conn` of
the page, after the app's own pipeline: mount the admin behind it, see
the [Admin](admin.md) guide). `c:resolve_access/1` says what they may
do:

  * `:all` - write, publish, delete, and change the settings;
  * `:read_only` - read the entries and the drafts, and change nothing;
  * `:forbidden` - nothing: the admin answers with a 404;
  * `{:forbidden, path}` - nothing: the admin sends them to `path`, such
    as the app's login page.

Tayori fails closed: there is no default, and a resolver without these
two functions does not compile.

## The readers

`c:resolve_reader/1` builds a `Tayori.Reader` from a `Plug.Conn` (the
JSON API and the public page) or from a LiveView socket (`Tayori.Live`,
after the app's own `on_mount` hooks, so `socket.assigns` has the app's
user). Nil is a visitor.

## The language of a visitor

The public page and the JSON API speak the reader's language; for a
visitor, the request's `?lang=`, else the browser's. An app that keeps
its own choice of language (a cookie, the session, its locale plug)
says it with `c:resolve_locale/1`, which then comes before the
browser's:

    @impl true
    def resolve_locale(_conn), do: Gettext.get_locale(MyAppWeb.Gettext)

# `access`

```elixir
@type access() :: :all | :read_only | :forbidden | {:forbidden, String.t()}
```

# `segment`

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

# `user`

```elixir
@type user() :: term()
```

# `author`
*optional* 

```elixir
@callback author(user()) :: map() | nil
```

Who wrote an entry, kept on it: a map with `"id"` and `"name"`. Without
this function, entries have no author.

# `resolve_access`

```elixir
@callback resolve_access(user()) :: access()
```

What the admin may do.

# `resolve_locale`
*optional* 

```elixir
@callback resolve_locale(Plug.Conn.t()) :: String.t() | nil
```

The language the app speaks to the request, or nil: for a visitor of
the public page and the JSON API, after `?lang=` and before the
browser's `Accept-Language`. A locale of the app's that the entries are
not written in takes one of the same language, else the first.

# `resolve_reader`
*optional* 

```elixir
@callback resolve_reader(Plug.Conn.t() | Phoenix.LiveView.Socket.t()) ::
  Tayori.Reader.t() | nil
```

The reader of a request or a LiveView, or nil for a visitor.

# `resolve_user`

```elixir
@callback resolve_user(Plug.Conn.t()) :: user()
```

The admin of the request, or nil.

# `segments`
*optional* 

```elixir
@callback segments() :: [segment() | {String.t(), [segment()]}]
```

The segments of readers an entry can be for, as the admin offers them:
a list of `{segment, label}`, or of `{group, [{segment, label}]}`.
Without this function, every entry is for everyone.

---

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