AuroraMeter. Entitlements
(Aurora Meter v0.4.0)
View Source
Plan resolution and the entitlement gate.
Semantics (see plan.md D12): a :hard limit blocks at its cap; a :metered
feature is always allowed (overage is billed); a :counter is always allowed
and never billed (ADR 0006); a {:feature, false} is denied; an undeclared
feature is permissive. with_quota/4 reserves atomically so hard
limits are correct under concurrency, releasing the reservation if the wrapped
function raises.
Plans resolve through AuroraMeter.Subscriptions (cached), and only a
subscription in an entitled status (AuroraMeter.Schema.Subscription.entitled_statuses/0)
grants its plan; anything else gets the default plan.
Summary
Types
Result of an entitlement check.
A dashboard-ready view of one feature's quota. kind is :hard, :metered,
:counter, :boolean, :feature (an integer plan value, carried in value)
or :undeclared; limit is set for hard caps, included for metered
allowances; percent is used relative to whichever applies (nil when neither
does, which includes every :counter — see ADR 0006).
Functions
Whether check/2 currently returns :ok.
Checks whether tenant may use feature right now.
Whether the tenant's plan grants access to feature at all (ignores quota).
The value of a feature :name, value declaration on tenant's plan, or
default when the plan does not declare it (or declares it as a limit or a
metered feature). Booleans and non-negative integers are both values
Returns the plan for tenant: its subscription's plan when the subscription is
in an entitled status, else the default plan.
A dashboard-ready snapshot of feature for tenant: kind, usage, cap or
allowance, remaining, overage, percentage and the current period.
Remaining quota for a hard-limited feature, or :unlimited — which is what a
metered feature, a counter, a plain feature and an undeclared feature all
report, because none of them has a cap to count down from.
Atomically reserves qty of feature against the plan (increments the
counter).
Assigns plan_id to tenant locally (no billing provider).
Gates, runs, and meters in one atomic step.
Types
@type check_result() :: :ok | {:error, :limit_exceeded | :not_entitled}
Result of an entitlement check.
@type quota() :: %{ feature: atom(), kind: :hard | :metered | :counter | :boolean | :feature | :undeclared, enabled: boolean(), value: non_neg_integer() | nil, used: integer(), limit: non_neg_integer() | nil, included: non_neg_integer() | nil, unit_price: number() | nil, remaining: non_neg_integer() | :unlimited, overage: non_neg_integer(), percent: non_neg_integer() | nil, period: AuroraMeter.Period.t() }
A dashboard-ready view of one feature's quota. kind is :hard, :metered,
:counter, :boolean, :feature (an integer plan value, carried in value)
or :undeclared; limit is set for hard caps, included for metered
allowances; percent is used relative to whichever applies (nil when neither
does, which includes every :counter — see ADR 0006).
Functions
Whether check/2 currently returns :ok.
@spec check(term(), atom()) :: check_result()
Checks whether tenant may use feature right now.
Whether the tenant's plan grants access to feature at all (ignores quota).
@spec feature_value(term(), atom(), default) :: boolean() | non_neg_integer() | default when default: term()
The value of a feature :name, value declaration on tenant's plan, or
default when the plan does not declare it (or declares it as a limit or a
metered feature). Booleans and non-negative integers are both values:
AuroraMeter.feature_value(org, :seats, 1) # 5 on :pro, 1 on :free
AuroraMeter.feature_value(org, :api_access) # true | false | nil
@spec plan(term()) :: AuroraMeter.Plan.t() | nil
Returns the plan for tenant: its subscription's plan when the subscription is
in an entitled status, else the default plan.
A dashboard-ready snapshot of feature for tenant: kind, usage, cap or
allowance, remaining, overage, percentage and the current period.
@spec remaining(term(), atom()) :: non_neg_integer() | :unlimited
Remaining quota for a hard-limited feature, or :unlimited — which is what a
metered feature, a counter, a plain feature and an undeclared feature all
report, because none of them has a cap to count down from.
@spec reserve(term(), atom(), pos_integer(), DateTime.t() | nil) :: :ok | {:error, :limit_exceeded | :not_entitled}
Atomically reserves qty of feature against the plan (increments the
counter).
period_start names the period to count it against; without it the current
one is used. A caller that will release the reservation later has to hold on
to the period it reserved in — see with_quota/4.
@spec subscribe(term(), atom() | String.t()) :: {:ok, AuroraMeter.Schema.Subscription.t()} | {:error, Ecto.Changeset.t()}
Assigns plan_id to tenant locally (no billing provider).
@spec with_quota(term(), atom(), (-> result)) :: {:ok, result} | {:error, term()} when result: term()
Gates, runs, and meters in one atomic step.
Reserves qty of feature; if allowed, runs fun and returns {:ok, result}
(the reservation is the usage). If the reservation is denied, returns
{:error, reason} without running fun. If fun raises, the reservation is
released and the error re-raised.
@spec with_quota(term(), atom(), pos_integer(), (-> result)) :: {:ok, result} | {:error, term()} when result: term()