AshDyan.Request (AshDyan v0.3.0)

Copy Markdown View Source

The runtime request spec for a dynamic analysis.

A caller sends a map (or a t/0 struct) describing what chart data they want. normalize/1 fills in defaults and validate/1 checks it against the resource's dyan DSL whitelist.

Shape

%{
  domain: MyApp.Shop,
  resource: MyApp.Order,
  type: :time_bucket,          # :frequency | :aggregate | :time_bucket | :percentile | :histogram
  column: :total_amount,
  function: :sum,               # required for :aggregate
  bucket: :day,                 # required for :time_bucket
  time_field: :inserted_at,
  group_by: [:status],          # optional, checked against max_group_by
  percentiles: [50, 90],        # required for :percentile
  bins: 10,                     # optional for :histogram (default 10)
  bin_width: nil,               # optional for :histogram (auto-computed if nil)
  filters: %{status: "paid", region: ["EU", "US"]},
  limit: 200,
  # Presentation options (apply to every type):
  sort_by: :value,              # :value | :label
  sort_order: :desc,            # :asc | :desc
  top: nil,                     # keep the N largest slices, roll the rest into "Other"
  cumulative: false,            # running totals (time_bucket)
  normalize: nil                # :percentage to convert series to share-of-total
}

Filters

filters are parsed by Ash.Filter.parse/2, so they support the full range of operators Ash understands on whitelisted fields — not just equality. For example:

%{status: "paid"}                       # equality
%{region: ["EU", "US"]}                 # membership (in)
%{total_amount: %{gt: 100}}             # comparison
%{inserted_at: %{between: [start, end]}} # range

Only fields declared in allow_filters_on may be filtered.

Summary

Functions

Normalize a request map into a t/0 struct with defaults applied.

Validate a normalized request against the resource's dyan DSL whitelist.

Types

analysis_type()

@type analysis_type() ::
  :frequency | :aggregate | :time_bucket | :percentile | :histogram

sort_by()

@type sort_by() :: :value | :label

sort_order()

@type sort_order() :: :asc | :desc

t()

@type t() :: %AshDyan.Request{
  bin_width: number() | nil,
  bins: pos_integer() | nil,
  bucket: AshDyan.time_bucket() | nil,
  column: atom() | nil,
  cumulative: boolean(),
  domain: module() | nil,
  filters: map(),
  function: AshDyan.aggregate_function() | nil,
  group_by: [atom()],
  limit: pos_integer() | nil,
  normalize: :percentage | nil,
  percentiles: [pos_integer()],
  resource: module(),
  sort_by: sort_by() | nil,
  sort_order: sort_order(),
  time_field: atom() | nil,
  top: pos_integer() | nil,
  type: analysis_type()
}

Functions

normalize(request)

@spec normalize(map() | t()) :: {:ok, t()} | {:error, term()}

Normalize a request map into a t/0 struct with defaults applied.

validate(request)

@spec validate(t()) :: :ok | {:error, AshDyan.Error.t()}

Validate a normalized request against the resource's dyan DSL whitelist.

Returns :ok or {:error, %AshDyan.Error{}} naming the offending field.

Error reasons

The reason field of the returned AshDyan.Error is one of:

  • :not_a_resource / :not_analyzable — the :resource is invalid or has no dyan configuration.
  • :unknown_type:type is not one of the capabilities.
  • :not_analyzable:column/time_field is not whitelisted for the type.
  • :not_allowed:function/:bucket/:percentiles is not in the whitelist for that field, or :filters references a non-allowed field.
  • :too_many:group_by exceeds max_group_by.
  • :too_large:limit exceeds max_limit.
  • :bad_bins:bins/:bin_width is of the wrong type or non-positive.
  • :unknown_attribute:group_by references a non-existent attribute.
  • :bad_type:filters/limit is of the wrong type.