# `WardrikRest.API`

An API loaded for agents: the operations offered from its description, where it's reached, and
where each credential comes from.

    {:ok, analytics} =
      WardrikRest.API.load(discovery,
        name: "ga4",
        base_url: "https://analyticsdata.googleapis.com/",
        auth: %{"oauth2" => {:credential, "google"}},
        only: ["properties_runReport", "properties_getMetadata"],
        read_only: ["properties_run*"]
      )

Load it once, as your app starts or adds the API, and give it to agents as
`{WardrikRest, apis: [analytics]}`.

## Where secrets come from

A source is one of:

  * `{:credential, name}` - the token of whoever the turn acts for, from the agent's
    `WardrikCore.Credentials`. Until they sign in, the call waits for them.
  * `{:key, name}` - a key from the agent's `WardrikCore.Keys`, such as the org's own.
  * A string, for a header only, sent as it is.

Secrets go only to `:base_url`, never into arguments, answers or errors.

## Options

* `:name` (`t:String.t/0`) - Required. The prefix of the tools' names, as `"ga4"` for `ga4__properties_runReport`.

* `:base_url` (`t:String.t/0`) - Required. Where the API is reached, as `"https://analyticsdata.googleapis.com/"`. Never read from the description.

* `:auth` (map of `t:String.t/0` keys and `t:term/0` values) - The source of each scheme the description names, as `%{"oauth2" => {:credential, "google"}}`. The default value is `%{}`.

* `:schemes` (map of `t:String.t/0` keys and `t:term/0` values) - Schemes the description doesn't name, each sent on every operation before its own, as `%{"token" => :bearer}`, `{:header, name}` or `{:query, name}`. `:auth` gives each a source. The default value is `%{}`.

* `:headers` (map of `t:String.t/0` keys and `t:term/0` values) - Headers every call sends that the description doesn't name, each a source, as `%{"developer-token" => {:key, "ADS_TOKEN"}}`. The default value is `%{}`.

* `:argument_headers` (map of `t:String.t/0` keys and `t:String.t/0` values) - Headers each tool takes as optional arguments, by name with what they're for, as `%{"login-customer-id" => "The manager account's ID"}`. The default value is `%{}`.

* `:only` (list of `t:String.t/0`) - The operations to offer, by name or glob, as `["properties_run*"]`. Every operation it can sign in to unless set. The default value is `["*"]`.

* `:read_only` (list of `t:String.t/0`) - Operations that change nothing beyond `GET`s, by name or glob. Many APIs read with `POST`. The default value is `[]`.

* `:idempotent` (list of `t:String.t/0`) - Operations safe to repeat beyond `GET`, `PUT` and `DELETE`, by name or glob. The default value is `[]`.

* `:trusted` (`t:boolean/0`) - `true` stops labelling answers `"outside"`. The default value is `false`.

* `:timeout` (`t:pos_integer/0`) - How long a call may take, in milliseconds. The default value is `30000`.

* `:max_result_bytes` (`t:pos_integer/0`) - Bytes of an answer the model gets, as JSON. A longer one is cut, with a note to ask for less. The default value is `131072`.

* `:max_schema_bytes` (`t:pos_integer/0`) - Bytes of each tool's schema, as JSON, where it can be. Types deeper than fit are named instead, and the `<name>__describe` tool gives their fields. The default value is `16384`.

* `:egress` (function of arity 1) - Checks `:base_url`, returning `:ok` or `{:error, reason}`. Only `https` unless set; Wardrik's is `Wardrik.Egress.public/1`. The default value is `&WardrikRest.API.https/1`.

* `:req_options` (`t:keyword/0`) - Merged into every request. The default value is `[]`.

# `source`

```elixir
@type source() :: {:credential, String.t()} | {:key, String.t()}
```

# `t`

```elixir
@type t() :: %WardrikRest.API{
  auth: %{required(String.t()) =&gt; source()},
  base_url: String.t(),
  described: [String.t()],
  headers: %{required(String.t()) =&gt; source() | String.t()},
  idempotent: [String.t()],
  max_result_bytes: pos_integer(),
  name: String.t(),
  operations: [WardrikRest.Operation.t()],
  read_only: [String.t()],
  req_options: keyword(),
  schemas: %{required(String.t()) =&gt; map()},
  schemes: %{required(String.t()) =&gt; WardrikRest.Spec.place()},
  timeout: pos_integer(),
  trusted: boolean(),
  types: WardrikRest.Schema.types()
}
```

# `annotations`

```elixir
@spec annotations(t(), WardrikRest.Operation.t()) :: keyword()
```

Returns the annotations of `operation`'s tool: whether it's read-only and idempotent.

# `describe`

```elixir
@spec describe(t() | String.t()) :: String.t()
```

Returns the name of the tool that describes `api`'s types, as `ads__describe`.

# `https`

```elixir
@spec https(String.t()) :: :ok | {:error, String.t()}
```

Returns `:ok` for an `https` URL with a host. The default `:egress`.

# `load`

```elixir
@spec load(WardrikRest.Reader.t(), keyword()) :: {:ok, t()} | {:error, String.t()}
```

Reads `source`, a `WardrikRest.Reader` and its options, and keeps the operations `opts` offer.

Returns `{:error, reason}` if the description can't be read, `:base_url` fails `:egress`,
`:auth` names a scheme neither the description nor `:schemes` has, or `:only` names an
operation it doesn't have, can't sign in to, or whose tool name would be over 64 characters. An
operation only a glob offers is left out where its tool name would be over 64 characters.

# `schema`

```elixir
@spec schema() :: NimbleOptions.t()
```

Returns the options `load/2` takes, to check them before loading.

# `security`

```elixir
@spec security(t(), WardrikRest.Operation.t()) :: {:ok, [String.t()]} | :error
```

Returns the schemes of the first of `operation`'s alternatives the API has sources for, or
`:error`.

# `tool_name`

```elixir
@spec tool_name(String.t(), WardrikRest.Operation.t()) :: String.t()
```

Returns the name of `operation`'s tool, as `ga4__properties_runReport`.

---

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