DpExchange.Core.Options (DpExchangeCore v0.3.120)

Copy Markdown View Source

Refuses a facade option the call does not read.

A misspelled limt: or sinse: that is silently ignored returns a plausible answer to a different question than the caller asked: the "nearby substitute where there should be an error" this family exists to avoid. Every facade function that takes a keyword opts calls check/2 at the boundary, before anything is sent and before an internal key (:limiter and the like) is added, so a stray key is a caller error.

Where it sits in the check order

Positional arguments first (subscriber, symbols, symbol, range), then the options, then credentials. An option error is decided before anything is sent, so it is a plain {:error, _}, never :order_unconfirmed. A function that answers Venue.not_supported() keeps doing so whatever the options: the endpoint's absence is the more useful answer.

What is allowed

transport_options/0 is the shared vocabulary, not a list every call accepts. A call that sends an HTTP request allows transport_options() -- not_read plus the keys it reads itself, where not_read names each shared key that call cannot honour (no sandbox: :environment; no URL override: :base_url), and every key it allows reaches Core.HttpClient or the seam that reads it. A call that sends no request (the feed and local reads) allows its own keys plus :credentials only. :credentials is exempt because a consumer hands one credential set to every call, and one a call does not need changes no answer. A nil-valued key is still a key: a forwarded limt: nil is refused too, because a caller who typed the key meant something by it.

The earlier rule, "every call allows every transport key", rested on a claim that every venue forwards them all. Measured in tick AK, it was false in three: environment: :sandbox sent Robinhood orders to production, Schwab ignored base_url: while sending a live token, and max_body_bytes: bounded nothing on two venues' REST calls.

What it answers

  • :ok
  • {:error, {:unknown_option, key}} - the first key (in the caller's order) not allowed
  • {:error, {:invalid_options, opts}} - opts is not a keyword list. Any :credentials entry of opts is replaced by :redacted in the tuple, since the error is logged.

Summary

Functions

Checks opts against the keys allowed.

The shared transport keys, whatever the venue: their names and meanings.

Functions

check(opts, allowed)

@spec check(term(), [atom()]) ::
  :ok | {:error, {:unknown_option, atom()} | {:invalid_options, term()}}

Checks opts against the keys allowed.

Answers :ok, {:error, {:unknown_option, key}} for the first key not in allowed (a key holding nil counts), or {:error, {:invalid_options, opts}} when opts is not a keyword list.

Examples

iex> DpExchange.Core.Options.check([limit: 5], [:limit])
:ok

iex> DpExchange.Core.Options.check([limt: 5], [:limit])
{:error, {:unknown_option, :limt}}

iex> DpExchange.Core.Options.check([limt: nil], [:limit])
{:error, {:unknown_option, :limt}}

iex> DpExchange.Core.Options.check(%{limit: 5}, [:limit])
{:error, {:invalid_options, %{limit: 5}}}

transport_options()

@spec transport_options() :: [atom()]

The shared transport keys, whatever the venue: their names and meanings.

Credentials, the environment and base URL, the Req test seams (:plug, :adapter, :req_adapter), and the limiter, timeout and retry knobs Core.HttpClient reads. A call that sends a request checks opts against (transport_options() -- not_read) ++ own_keys; a call that sends none against [:credentials | own_keys] (see the moduledoc).