Validates config :threadline, :retention before purge runs.
Threadline supports a single global retention window (:keep_days or
:max_age_seconds, mutually exclusive) plus an enabled flag that must
be true for destructive purge. delete_empty_transactions defaults to
true (remove parent audit_transactions rows with no remaining children
after change deletes).
Per-table and per-tenant overrides are not supported; this module validates the global policy shape only.
validate_config!/1 checks configuration, resolve!/1 returns normalized
policy values, and cutoff_utc_datetime_usec!/1 computes the expiry cutoff.
Summary
Types
The keyword-list or map form accepted by retention policy validation and resolution.
A retention config map. Recognized keys are atom or string spellings of :enabled,
:delete_empty_transactions, :keep_days, and :max_age_seconds. The boolean keys accept
booleans or the strings "true" and "false"; window values accept positive integers.
Other keys are ignored, and the string-key map arm represents extra keys and mixed atom/string
maps.
An atom-keyed option in the retention policy configuration.
An option accepted by cutoff_utc_datetime_usec!/1.
Normalized retention options as returned by resolve/1.
Functions
Returns UTC DateTime strictly before which AuditChange.captured_at values
are considered expired for purge (i.e. delete rows with captured_at < cutoff).
Resolves config into a struct or raises like validate_config!/1.
Returns :ok when retention config from Application.get_env(:threadline, :retention) is valid.
Types
@type config() :: [config_opt()] | config_map()
The keyword-list or map form accepted by retention policy validation and resolution.
@type config_map() :: %{ optional(:enabled) => boolean() | String.t(), optional(:delete_empty_transactions) => boolean() | String.t(), optional(:keep_days) => pos_integer() | nil | false, optional(:max_age_seconds) => pos_integer() | nil | false } | %{optional(String.t()) => term()}
A retention config map. Recognized keys are atom or string spellings of :enabled,
:delete_empty_transactions, :keep_days, and :max_age_seconds. The boolean keys accept
booleans or the strings "true" and "false"; window values accept positive integers.
Other keys are ignored, and the string-key map arm represents extra keys and mixed atom/string
maps.
For boolean keys, a present atom key wins over its string spelling even when its value is
invalid. For window keys, atom_value || string_value is used, so atom nil or false falls back
to the matching string key while 0 or another truthy invalid atom value keeps its validation
error. Positive window values remain mutually exclusive; when both are absent, the test
environment uses a one-day default.
@type config_opt() :: {:enabled, boolean() | String.t()} | {:delete_empty_transactions, boolean() | String.t()} | {:keep_days, pos_integer() | nil | false} | {:max_age_seconds, pos_integer() | nil | false}
An atom-keyed option in the retention policy configuration.
@type cutoff_opt() :: {:policy, t()}
An option accepted by cutoff_utc_datetime_usec!/1.
@type t() :: %Threadline.Retention.Policy{ delete_empty_transactions: boolean(), enabled: boolean(), window_seconds: pos_integer() }
Normalized retention options as returned by resolve/1.
Functions
@spec cutoff_utc_datetime_usec!([cutoff_opt()]) :: DateTime.t()
Returns UTC DateTime strictly before which AuditChange.captured_at values
are considered expired for purge (i.e. delete rows with captured_at < cutoff).
Uses DateTime.add/3 in microsecond mode for consistency with :utc_datetime_usec.
Options
:policy— normalized retention policy. Optional. Defaults to the configured policy.
Returns
- The UTC cutoff timestamp.
Other option keys are ignored.
Resolves config into a struct or raises like validate_config!/1.
@spec validate_config!(config()) :: :ok
Returns :ok when retention config from Application.get_env(:threadline, :retention) is valid.
Raises ArgumentError with a message containing "retention" when either boolean option is
not a boolean or its string spelling, when both window keys are set, or when a window is
non-positive or missing outside the test environment.
In :test, missing :keep_days / :max_age_seconds is allowed only when the
caller passes a non-empty map/list that still fails other checks — for empty
config in test, hosts should set explicit values in config/test.exs.