# `Unicom360.Client`
[🔗](https://github.com/TechRode-Solution-Limited/unicom360-elixir/blob/v0.1.0/lib/unicom360/client.ex#L1)

A configured, immutable Unicom360 client.

Build one with `Unicom360.client/1` (or `Unicom360.client!/1`) and pass it to every operation.
A client is a plain struct: it holds no process and is safe to share between processes and to
keep in application config or a module attribute at runtime. `put_options/2` returns a copy
with other options.

The API key is never shown by `inspect/2`:

    #Unicom360.Client<base_url: "https://api.unicom360.com", api_key: [REDACTED], ...>

## Options

  * `:api_key` - required. Defaults to the `:unicom360` application environment, then the
    `UNICOM360_API_KEY` environment variable. A missing, empty or blank key is a
    `:configuration_error`. An explicit `api_key: ""` does not fall back to the environment.
  * `:base_url` - defaults to the application environment, then `UNICOM360_BASE_URL`, then
    `"https://api.unicom360.com"`. Must be `https`; plain `http` is allowed only for
    `localhost`, `127.0.0.0/8` and `::1` (a local mock). A path prefix is kept, trailing
    slashes are removed, and a query string or fragment is refused.
  * `:timeout` - milliseconds to wait for the response on each attempt (Req's
    `:receive_timeout`). Default `60_000`.
  * `:connect_timeout` - milliseconds to connect, TLS included. Default `10_000`.
  * `:max_retries` - retries after the first attempt. Default `2`.
  * `:max_retry_wait` - the longest single wait between attempts, in milliseconds. Default
    `60_000`. A rate-limit wait longer than this returns the error at once.
  * `:auto_reference_ids` - give every send without a `user_reference_id` a generated one,
    reused on every retry. Default `false`. See the README.
  * `:app_info` - `%{name: "shopsync", version: "2.3.0", url: "https://shopsync.example"}`
    (version and url optional), appended to the User-Agent.
  * `:req_options` - extra options for `Req.new/1` (for example `:finch`, `:connect_options`
    or a custom `:adapter`). The SDK always sets `retry: false` (it retries itself),
    `redirect: false` (the API key is never replayed to another host) and its own method,
    URL, body and headers.
  * `:plug` - a plug that answers instead of the network, for tests (`Req.Test`).
  * `:clock` - a 0-arity function returning the current Unix time in milliseconds. Used for
    rate-limit waits. Test seam.
  * `:sleeper` - a 1-arity function taking milliseconds; every retry wait goes through it.
    Test seam.
  * `:random` - a 0-arity function returning a float in `[0, 1)`, for jitter. Test seam.

`:api_key`, `:base_url`, `:timeout`, `:connect_timeout`, `:max_retries`, `:max_retry_wait`,
`:auto_reference_ids`, `:app_info` and `:req_options` can also be set in the application
environment:

    # config/runtime.exs
    config :unicom360, api_key: System.get_env("UNICOM360_API_KEY")

An option passed to `Unicom360.client/1` wins over the application environment, which wins
over the environment variables, which win over the defaults. A value of `nil`, and an
environment variable that is empty or blank, count as unset.

# `app_info`

```elixir
@type app_info() :: %{
  name: String.t(),
  version: String.t() | nil,
  url: String.t() | nil
}
```

`%{name: String.t(), version: String.t() | nil, url: String.t() | nil}`

# `option`

```elixir
@type option() ::
  {:api_key, String.t()}
  | {:base_url, String.t()}
  | {:timeout, pos_integer()}
  | {:connect_timeout, pos_integer()}
  | {:max_retries, non_neg_integer()}
  | {:max_retry_wait, non_neg_integer()}
  | {:auto_reference_ids, boolean()}
  | {:app_info, map() | keyword()}
  | {:req_options, keyword()}
  | {:plug, term()}
  | {:clock, (-&gt; integer())}
  | {:sleeper, (non_neg_integer() -&gt; term())}
  | {:random, (-&gt; float())}
```

# `t`

```elixir
@type t() :: %Unicom360.Client{
  api_key: String.t(),
  app_info: app_info() | nil,
  auto_reference_ids: boolean(),
  base_url: String.t(),
  clock: (-&gt; integer()) | nil,
  connect_timeout: pos_integer(),
  max_retries: non_neg_integer(),
  max_retry_wait: non_neg_integer(),
  plug: term(),
  random: (-&gt; float()) | nil,
  req_options: keyword(),
  sleeper: (non_neg_integer() -&gt; term()) | nil,
  timeout: pos_integer(),
  user_agent: String.t()
}
```

# `put_options`

```elixir
@spec put_options(t(), [option()]) :: t()
```

Returns a copy of the client with some options changed.

Takes the same options as `Unicom360.client/1`; the ones not given keep their current value
(the environment is not read again). Raises `Unicom360.Error` (`:configuration_error`) for an
unusable option.

    fast = Unicom360.Client.put_options(client, timeout: 5_000, max_retries: 0)

---

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