StatifierRouter.Binding (StatifierRouter v0.9.2)

Copy Markdown View Source

A binding: the declaration that routes an event from a source to one execution of one document, as ADR-0001 fixes it.

A binding says which events it wants (match), how to compute the key that names one execution among many (key), which document that execution belongs to (document), which chart event the execution is handed (event), and which fields of the event travel with it (data).

Construction

new/1 takes a map or keyword list whose keys are atoms from this table (ADR-0001, section 1) and returns {:ok, binding} or {:error, reason}:

KeyValueDefault
:ida non-empty stringrequired
:sourcea stringrequired
:selectora map%{}
:matcha predicator program, as a stringrequired
:keya predicator program, as a stringrequired
:documenta non-empty stringrequired
:eventa non-empty stringrequired
:dataa list of dotted field paths, as strings[]
:create:if_absent, :never or :always_new:if_absent
:dedupe%{by: :message_id, horizon_ms: h}, h a positive integer%{by: :message_id, horizon_ms: 259_200_000}
:order:by_key or :none:by_key
:enableda booleantrue

Every fault that can be seen without an event is refused here, in this order: a reserved key (mode, batch or window, ADR-0001 section 6) before any other check, then an unknown key, then a missing required key, then a value the table does not allow, then a match or key that predicator does not compile (ADR-0001 section 2). match and key are compiled once, here, and kept compiled.

A duplicate id among several bindings is a fault of the configuration that holds them, not of one binding, so new/1 does not look for it.

Evaluation

match/2 and key/2 evaluate the compiled programs over the normalized event, a string-keyed map bound in the predicator context as event, so a program reads event.kind or event.impression_id.

  • match/2 returns true only when the program evaluates to exactly true. false and nil return false, and :undefined returns :undefined: all three mean the event is not for this binding. An evaluation error, or any other value, is {:refused, reason} (ADR-0001 section 2).
  • key/2 returns {:ok, key} when the program evaluates to a non-empty string, and {:refused, reason} for anything else (ADR-0001 section 3).

project/2 builds the delivered event's data from the data paths (ADR-0001 section 5).

Example

iex> {:ok, binding} =
...>   StatifierRouter.Binding.new(
...>     id: "clicks_to_join",
...>     source: "ad_events",
...>     match: "event.kind == 'click'",
...>     key: "event.impression_id",
...>     document: "impression_click_join",
...>     event: "click",
...>     data: ["impression_id", "url"]
...>   )
iex> click = %{"kind" => "click", "impression_id" => "imp_7f3a"}
iex> StatifierRouter.Binding.match(binding, click)
true
iex> StatifierRouter.Binding.key(binding, click)
{:ok, "imp_7f3a"}
iex> StatifierRouter.Binding.project(binding, click)
%{"impression_id" => "imp_7f3a"}

Summary

Types

Why new/1 refused a binding.

A predicator instruction list, as Predicator.compile/1 returns it.

Why match/2 or key/2 refused an event for this binding.

t()

Functions

Evaluates the binding's key over the normalized event.

Evaluates the binding's match over the normalized event.

Builds a binding from a map or keyword list with atom keys, validating every key and value and compiling match and key once.

Builds the delivered event's data: each of the binding's data paths, read from the normalized event and written under the same path.

Types

new_error()

@type new_error() ::
  {:reserved_key, term()}
  | {:unknown_key, term()}
  | {:duplicate_key, atom()}
  | {:missing_key, atom()}
  | {:invalid_value, atom(), term()}
  | {:match, struct()}
  | {:key, struct()}
  | {:invalid_binding, term()}

Why new/1 refused a binding.

program()

@type program() :: list()

A predicator instruction list, as Predicator.compile/1 returns it.

refusal()

@type refusal() ::
  {:evaluation_error, struct()}
  | {:non_boolean, term()}
  | {:invalid_key, term()}

Why match/2 or key/2 refused an event for this binding.

t()

@type t() :: %StatifierRouter.Binding{
  compiled_key: program(),
  compiled_match: program(),
  create: :if_absent | :never | :always_new,
  data: [String.t()],
  dedupe: %{by: :message_id, horizon_ms: pos_integer()},
  document: String.t(),
  enabled: boolean(),
  event: String.t(),
  id: String.t(),
  key: String.t(),
  match: String.t(),
  order: :by_key | :none,
  selector: map(),
  source: String.t()
}

Functions

key(binding, event)

@spec key(t(), map()) :: {:ok, String.t()} | {:refused, refusal()}

Evaluates the binding's key over the normalized event.

Returns {:ok, key} when the program evaluates to a non-empty string. Anything else - :undefined, nil, the empty string, a number, any other value, or an evaluation error - is {:refused, reason}.

match(binding, event)

@spec match(t(), map()) :: true | false | :undefined | {:refused, refusal()}

Evaluates the binding's match over the normalized event.

Returns true when the program evaluates to exactly true; false when it evaluates to false or nil; :undefined when it evaluates to :undefined. The last two mean the event is not for this binding. Returns {:refused, reason} when the evaluation returns an error or any other value.

new(attrs)

@spec new(map() | keyword()) :: {:ok, t()} | {:error, new_error()}

Builds a binding from a map or keyword list with atom keys, validating every key and value and compiling match and key once.

Returns {:ok, binding}, or {:error, reason} naming the first fault found, in the order the moduledoc gives.

project(binding, event)

@spec project(t(), map()) :: map()

Builds the delivered event's data: each of the binding's data paths, read from the normalized event and written under the same path.

A dotted path reads and writes nested maps, so "placement.slot" carries %{"placement" => %{"slot" => value}}. A path the event does not carry is left out, and nothing outside the listed paths is delivered.