meterbase 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meterbase-0.1.0 → meterbase-0.1.2}/PKG-INFO +46 -20
- {meterbase-0.1.0 → meterbase-0.1.2}/README.md +45 -19
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/customer_plan.py +9 -7
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/customers.py +3 -1
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/plans.py +7 -3
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/customer_plan.py +9 -7
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/customers.py +3 -1
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/plans.py +7 -3
- meterbase-0.1.2/src/meterbase/_version.py +1 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/errors.py +5 -5
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/types.py +21 -5
- meterbase-0.1.0/src/meterbase/_version.py +0 -1
- {meterbase-0.1.0 → meterbase-0.1.2}/.gitignore +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/LICENSE +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/pyproject.toml +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/__init__.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/__init__.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/_io.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/_transport.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/client.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/__init__.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/customer_allowances.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/customer_periods.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_async/resources/meters.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_http.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_json.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_keys.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_models.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_not_given.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/__init__.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/_io.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/_transport.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/client.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/__init__.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/customer_allowances.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/customer_periods.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_sync/resources/meters.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/_time.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/py.typed +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/resources.py +0 -0
- {meterbase-0.1.0 → meterbase-0.1.2}/src/meterbase/webhooks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: meterbase
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python SDK for the Meterbase engine
|
|
5
5
|
Project-URL: Homepage, https://meterbase.tech
|
|
6
6
|
Project-URL: Documentation, https://meterbase.tech/docs
|
|
@@ -81,10 +81,11 @@ denial — that is an answer, not a failure. Absence of entitlement is not
|
|
|
81
81
|
permission: a customer with no plan and no grant is denied.
|
|
82
82
|
|
|
83
83
|
`available` is the whole capacity — the plan's unused entitlement for the
|
|
84
|
-
current period
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
current period, with what is left of any [rollover](#plan-allowances) carry,
|
|
85
|
+
plus every open grant — and is `None` under `no_cap`, where capacity is not a
|
|
86
|
+
number. It is one figure and not a breakdown: `check` answers from a single
|
|
87
|
+
cached integer, and itemising where the capacity came from would cost it that.
|
|
88
|
+
For the per-grant detail, read a customer's allowances.
|
|
88
89
|
|
|
89
90
|
A check answers **two gates**, and `allowed` needs both: the capacity has to
|
|
90
91
|
cover the quantity, and every rate limit in force — _at most so much of this
|
|
@@ -392,6 +393,22 @@ is refused beside `no_cap` and on `one_time`. Like the amount, it is a term of
|
|
|
392
393
|
the version, so turning it on or off follows `apply_mode`: at each customer's
|
|
393
394
|
next cycle by default.
|
|
394
395
|
|
|
396
|
+
**Rollover** carries what a period leaves of `amount` into the next one, which
|
|
397
|
+
spends it first. Whatever that period leaves of the carry is lost, so a carry
|
|
398
|
+
never stacks.
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
meterbase.plans.allowances.set(plan.id, meter_id=meter.id, amount=1_000, rollover=True)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Both periods' versions must roll over, so moving to a plan that does not ends
|
|
405
|
+
the carry. A `restart` passes nothing on, and usage the carry paid reaches no
|
|
406
|
+
threshold. Read it as `carried_in` and `carried_used` on
|
|
407
|
+
[the entitlement](#how-much-they-have-used). `rollover` defaults to `False`,
|
|
408
|
+
goes beside `allow_overage` and on an amount of `0`, and is refused beside
|
|
409
|
+
`no_cap` and on `one_time`. It follows `apply_mode` too, and the first carry
|
|
410
|
+
arrives a boundary after the version lands.
|
|
411
|
+
|
|
395
412
|
### A customer's plan
|
|
396
413
|
|
|
397
414
|
A customer's plan is an append-only history, not a field: assigning is the only
|
|
@@ -448,10 +465,11 @@ is the wire shape underneath all four, and still takes `plan_id`.
|
|
|
448
465
|
|
|
449
466
|
#### Two plans on different cycles
|
|
450
467
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
`
|
|
468
|
+
Plans on different cycles share no period to keep, so `change_now` between
|
|
469
|
+
them raises `CycleChangeRequiresResetError`, with `"none"` or `"prorate"`
|
|
470
|
+
alike. A weekly plan and a month-based one share no boundary to wait for
|
|
471
|
+
either, so `change_at_next_cycle` between those two raises it too. `restart` is
|
|
472
|
+
the move that works: it starts a fresh period on the new cycle.
|
|
455
473
|
|
|
456
474
|
```python
|
|
457
475
|
from meterbase import CycleChangeRequiresResetError
|
|
@@ -473,8 +491,9 @@ trail = meterbase.customers.plan.history(customer.id).data
|
|
|
473
491
|
`retrieve` is the plan **in force now**, which is not always the newest
|
|
474
492
|
instruction — a change dated ahead does not govern yet — and is `None` for a
|
|
475
493
|
customer holding no plan. That is a default deny rather than a failure, so it
|
|
476
|
-
is an answer here rather than a raised `NotFoundError
|
|
477
|
-
|
|
494
|
+
is an answer here rather than a raised `NotFoundError`. An id that names no
|
|
495
|
+
customer reads as `None` too: the engine answers it as a customer holding no
|
|
496
|
+
plan.
|
|
478
497
|
|
|
479
498
|
`pending` is the change waiting to land, and at most one ever is: a newer
|
|
480
499
|
instruction supersedes the one before it. `history` is the whole trail,
|
|
@@ -504,12 +523,16 @@ for meter in entitlement.meters if entitlement else []:
|
|
|
504
523
|
|
|
505
524
|
`entitlement` is the plan's side of the current period, one entry per meter the
|
|
506
525
|
plan meters: the read behind a usage bar in your own app. `used` is what
|
|
507
|
-
`amount` is measured against, `total` adds what grants paid for,
|
|
508
|
-
`available` is what `check` would answer. `overage_used` is how far `used`
|
|
509
|
-
run past `amount` — what a [soft cap](#plan-allowances) bills for, and
|
|
510
|
-
under a hard cap too — and `None` where there is no amount;
|
|
511
|
-
says whether the cap in effect is soft.
|
|
512
|
-
|
|
526
|
+
`amount` is measured against, `total` adds what the carry and grants paid for,
|
|
527
|
+
and `available` is what `check` would answer. `overage_used` is how far `used`
|
|
528
|
+
has run past `amount` — what a [soft cap](#plan-allowances) bills for, and
|
|
529
|
+
reported under a hard cap too — and `None` where there is no amount;
|
|
530
|
+
`allow_overage` says whether the cap in effect is soft. `rollover` says whether
|
|
531
|
+
it [rolls over](#plan-allowances): `carried_in` is what the last period left of
|
|
532
|
+
its amount, `carried_used` what this one has spent of it, and
|
|
533
|
+
`carried_in - carried_used` what goes at the period's end. Both are `0` where
|
|
534
|
+
nothing rolled over and `None` where there is no amount. Meters are named by
|
|
535
|
+
id, and like `retrieve` it is `None` for a customer holding no plan.
|
|
513
536
|
|
|
514
537
|
Unlike `check`, it is not answered from a cache, so read it where usage is
|
|
515
538
|
shown rather than on every request.
|
|
@@ -518,7 +541,9 @@ shown rather than on every request.
|
|
|
518
541
|
|
|
519
542
|
`customers.periods.last` is the newest period that has ended, with the figures
|
|
520
543
|
to bill from: per meter, the cap `check` answered at its last instant, `used`
|
|
521
|
-
against it, and `overage_used`, which is what a soft cap bills
|
|
544
|
+
against it, and `overage_used`, which is what a soft cap bills. `carried_in`,
|
|
545
|
+
`carried_used` and `carried_out` are what a rolling allowance took from the
|
|
546
|
+
period before, spent, and passed to the next, and `total - used - carried_used`
|
|
522
547
|
is what grants absorbed. They come from the counters behind `check`, which
|
|
523
548
|
count each event exactly once. [Usage by day](#usage-by-day) is for where
|
|
524
549
|
usage went, not for billing.
|
|
@@ -854,8 +879,9 @@ exactly that much away, that a replay takes nothing, and that a reused key
|
|
|
854
879
|
replays whatever the body says; that a hold's commit records what the work cost
|
|
855
880
|
and gives the rest back; that a rate limit comes back on every `check` and
|
|
856
881
|
refuses with the allowance untouched; that a soft cap admits and holds past its
|
|
857
|
-
amount and reports the overage; that a
|
|
858
|
-
|
|
882
|
+
amount and reports the overage; that a rolling allowance carries nothing into a
|
|
883
|
+
first period or across a `restart`; that a period a `restart` closes settles
|
|
884
|
+
for a minute, which the run waits out, and then reads final; that plan changes
|
|
859
885
|
schedule, cancel and re-anchor as documented; and — reading each model's
|
|
860
886
|
annotations — that the engine's fields still match the SDK's types exactly, in
|
|
861
887
|
both directions.
|
|
@@ -54,10 +54,11 @@ denial — that is an answer, not a failure. Absence of entitlement is not
|
|
|
54
54
|
permission: a customer with no plan and no grant is denied.
|
|
55
55
|
|
|
56
56
|
`available` is the whole capacity — the plan's unused entitlement for the
|
|
57
|
-
current period
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
current period, with what is left of any [rollover](#plan-allowances) carry,
|
|
58
|
+
plus every open grant — and is `None` under `no_cap`, where capacity is not a
|
|
59
|
+
number. It is one figure and not a breakdown: `check` answers from a single
|
|
60
|
+
cached integer, and itemising where the capacity came from would cost it that.
|
|
61
|
+
For the per-grant detail, read a customer's allowances.
|
|
61
62
|
|
|
62
63
|
A check answers **two gates**, and `allowed` needs both: the capacity has to
|
|
63
64
|
cover the quantity, and every rate limit in force — _at most so much of this
|
|
@@ -365,6 +366,22 @@ is refused beside `no_cap` and on `one_time`. Like the amount, it is a term of
|
|
|
365
366
|
the version, so turning it on or off follows `apply_mode`: at each customer's
|
|
366
367
|
next cycle by default.
|
|
367
368
|
|
|
369
|
+
**Rollover** carries what a period leaves of `amount` into the next one, which
|
|
370
|
+
spends it first. Whatever that period leaves of the carry is lost, so a carry
|
|
371
|
+
never stacks.
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
meterbase.plans.allowances.set(plan.id, meter_id=meter.id, amount=1_000, rollover=True)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Both periods' versions must roll over, so moving to a plan that does not ends
|
|
378
|
+
the carry. A `restart` passes nothing on, and usage the carry paid reaches no
|
|
379
|
+
threshold. Read it as `carried_in` and `carried_used` on
|
|
380
|
+
[the entitlement](#how-much-they-have-used). `rollover` defaults to `False`,
|
|
381
|
+
goes beside `allow_overage` and on an amount of `0`, and is refused beside
|
|
382
|
+
`no_cap` and on `one_time`. It follows `apply_mode` too, and the first carry
|
|
383
|
+
arrives a boundary after the version lands.
|
|
384
|
+
|
|
368
385
|
### A customer's plan
|
|
369
386
|
|
|
370
387
|
A customer's plan is an append-only history, not a field: assigning is the only
|
|
@@ -421,10 +438,11 @@ is the wire shape underneath all four, and still takes `plan_id`.
|
|
|
421
438
|
|
|
422
439
|
#### Two plans on different cycles
|
|
423
440
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
`
|
|
441
|
+
Plans on different cycles share no period to keep, so `change_now` between
|
|
442
|
+
them raises `CycleChangeRequiresResetError`, with `"none"` or `"prorate"`
|
|
443
|
+
alike. A weekly plan and a month-based one share no boundary to wait for
|
|
444
|
+
either, so `change_at_next_cycle` between those two raises it too. `restart` is
|
|
445
|
+
the move that works: it starts a fresh period on the new cycle.
|
|
428
446
|
|
|
429
447
|
```python
|
|
430
448
|
from meterbase import CycleChangeRequiresResetError
|
|
@@ -446,8 +464,9 @@ trail = meterbase.customers.plan.history(customer.id).data
|
|
|
446
464
|
`retrieve` is the plan **in force now**, which is not always the newest
|
|
447
465
|
instruction — a change dated ahead does not govern yet — and is `None` for a
|
|
448
466
|
customer holding no plan. That is a default deny rather than a failure, so it
|
|
449
|
-
is an answer here rather than a raised `NotFoundError
|
|
450
|
-
|
|
467
|
+
is an answer here rather than a raised `NotFoundError`. An id that names no
|
|
468
|
+
customer reads as `None` too: the engine answers it as a customer holding no
|
|
469
|
+
plan.
|
|
451
470
|
|
|
452
471
|
`pending` is the change waiting to land, and at most one ever is: a newer
|
|
453
472
|
instruction supersedes the one before it. `history` is the whole trail,
|
|
@@ -477,12 +496,16 @@ for meter in entitlement.meters if entitlement else []:
|
|
|
477
496
|
|
|
478
497
|
`entitlement` is the plan's side of the current period, one entry per meter the
|
|
479
498
|
plan meters: the read behind a usage bar in your own app. `used` is what
|
|
480
|
-
`amount` is measured against, `total` adds what grants paid for,
|
|
481
|
-
`available` is what `check` would answer. `overage_used` is how far `used`
|
|
482
|
-
run past `amount` — what a [soft cap](#plan-allowances) bills for, and
|
|
483
|
-
under a hard cap too — and `None` where there is no amount;
|
|
484
|
-
says whether the cap in effect is soft.
|
|
485
|
-
|
|
499
|
+
`amount` is measured against, `total` adds what the carry and grants paid for,
|
|
500
|
+
and `available` is what `check` would answer. `overage_used` is how far `used`
|
|
501
|
+
has run past `amount` — what a [soft cap](#plan-allowances) bills for, and
|
|
502
|
+
reported under a hard cap too — and `None` where there is no amount;
|
|
503
|
+
`allow_overage` says whether the cap in effect is soft. `rollover` says whether
|
|
504
|
+
it [rolls over](#plan-allowances): `carried_in` is what the last period left of
|
|
505
|
+
its amount, `carried_used` what this one has spent of it, and
|
|
506
|
+
`carried_in - carried_used` what goes at the period's end. Both are `0` where
|
|
507
|
+
nothing rolled over and `None` where there is no amount. Meters are named by
|
|
508
|
+
id, and like `retrieve` it is `None` for a customer holding no plan.
|
|
486
509
|
|
|
487
510
|
Unlike `check`, it is not answered from a cache, so read it where usage is
|
|
488
511
|
shown rather than on every request.
|
|
@@ -491,7 +514,9 @@ shown rather than on every request.
|
|
|
491
514
|
|
|
492
515
|
`customers.periods.last` is the newest period that has ended, with the figures
|
|
493
516
|
to bill from: per meter, the cap `check` answered at its last instant, `used`
|
|
494
|
-
against it, and `overage_used`, which is what a soft cap bills
|
|
517
|
+
against it, and `overage_used`, which is what a soft cap bills. `carried_in`,
|
|
518
|
+
`carried_used` and `carried_out` are what a rolling allowance took from the
|
|
519
|
+
period before, spent, and passed to the next, and `total - used - carried_used`
|
|
495
520
|
is what grants absorbed. They come from the counters behind `check`, which
|
|
496
521
|
count each event exactly once. [Usage by day](#usage-by-day) is for where
|
|
497
522
|
usage went, not for billing.
|
|
@@ -827,8 +852,9 @@ exactly that much away, that a replay takes nothing, and that a reused key
|
|
|
827
852
|
replays whatever the body says; that a hold's commit records what the work cost
|
|
828
853
|
and gives the rest back; that a rate limit comes back on every `check` and
|
|
829
854
|
refuses with the allowance untouched; that a soft cap admits and holds past its
|
|
830
|
-
amount and reports the overage; that a
|
|
831
|
-
|
|
855
|
+
amount and reports the overage; that a rolling allowance carries nothing into a
|
|
856
|
+
first period or across a `restart`; that a period a `restart` closes settles
|
|
857
|
+
for a minute, which the run waits out, and then reads final; that plan changes
|
|
832
858
|
schedule, cancel and re-anchor as documented; and — reading each model's
|
|
833
859
|
annotations — that the engine's fields still match the SDK's types exactly, in
|
|
834
860
|
both directions.
|
|
@@ -96,8 +96,9 @@ class AsyncCustomerPlan:
|
|
|
96
96
|
plan they are paying for until it runs out, and the new one starts at
|
|
97
97
|
their next boundary. Nothing is pro-rated, because nothing is split.
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
`CycleChangeRequiresResetError
|
|
99
|
+
A weekly plan and a month-based one share no boundary, and between
|
|
100
|
+
them this raises `CycleChangeRequiresResetError`: use `restart` for
|
|
101
|
+
that move. Other cycles land on the coarser plan's boundary.
|
|
101
102
|
"""
|
|
102
103
|
return await self.change(customer_id, plan=plan, effective="next_cycle")
|
|
103
104
|
|
|
@@ -117,10 +118,11 @@ class AsyncCustomerPlan:
|
|
|
117
118
|
period. Usage already spent still counts, so a downgrade can deny
|
|
118
119
|
until the period ends.
|
|
119
120
|
- `prorate` — each plan's amount weighted by the fraction of the period
|
|
120
|
-
it was held for.
|
|
121
|
-
share no period to weight.
|
|
121
|
+
it was held for.
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
Between plans on different cycles both are refused with
|
|
124
|
+
`CycleChangeRequiresResetError`: there is no period to keep, so use
|
|
125
|
+
`restart`. To have the period itself start over, use `restart` too.
|
|
124
126
|
"""
|
|
125
127
|
return await self.change(
|
|
126
128
|
customer_id,
|
|
@@ -170,8 +172,8 @@ class AsyncCustomerPlan:
|
|
|
170
172
|
|
|
171
173
|
`None` when the customer holds no plan. That is a default deny rather
|
|
172
174
|
than a failure — they are entitled to nothing — so it is an answer here
|
|
173
|
-
and not a raised `NotFoundError`.
|
|
174
|
-
|
|
175
|
+
and not a raised `NotFoundError`. An id that names no customer answers
|
|
176
|
+
`None` too: the engine reads it as a customer holding no plan.
|
|
175
177
|
"""
|
|
176
178
|
try:
|
|
177
179
|
return await self._api.request(
|
|
@@ -98,7 +98,9 @@ class AsyncCustomers:
|
|
|
98
98
|
return found.data[0] if found.data else None
|
|
99
99
|
|
|
100
100
|
async def entitlement(self, id: str) -> CustomerEntitlement | None:
|
|
101
|
-
"""`None` for a customer holding no plan, as `plan.retrieve` answers
|
|
101
|
+
"""`None` for a customer holding no plan, as `plan.retrieve` answers,
|
|
102
|
+
and for an id that names no customer, which the engine reads the same
|
|
103
|
+
way."""
|
|
102
104
|
try:
|
|
103
105
|
return await self._api.request(
|
|
104
106
|
"GET",
|
|
@@ -32,6 +32,7 @@ class AsyncPlanAllowances:
|
|
|
32
32
|
amount: int | NotGiven = NOT_GIVEN,
|
|
33
33
|
no_cap: bool | NotGiven = NOT_GIVEN,
|
|
34
34
|
allow_overage: bool | NotGiven = NOT_GIVEN,
|
|
35
|
+
rollover: bool | NotGiven = NOT_GIVEN,
|
|
35
36
|
kind: PlanAllowanceKind | NotGiven = NOT_GIVEN,
|
|
36
37
|
valid_for: ValidityParam | Validity | NotGiven = NOT_GIVEN,
|
|
37
38
|
apply_mode: ApplyMode | NotGiven = NOT_GIVEN,
|
|
@@ -41,9 +42,11 @@ class AsyncPlanAllowances:
|
|
|
41
42
|
cycle. Give exactly one of `amount` or `no_cap`.
|
|
42
43
|
|
|
43
44
|
`allow_overage` is a soft cap: past `amount`, admit rather than refuse.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
`rollover` carries what a period leaves of `amount` into the next one,
|
|
46
|
+
which spends it first. Both default to `False` and are refused beside
|
|
47
|
+
`no_cap` and on `one_time`. `kind` defaults to `recurring`. `valid_for`
|
|
48
|
+
is `one_time` only. `apply_mode` defaults to `next_cycle`, except
|
|
49
|
+
`one_time` which is always immediate.
|
|
47
50
|
"""
|
|
48
51
|
return await self._api.request(
|
|
49
52
|
"POST",
|
|
@@ -54,6 +57,7 @@ class AsyncPlanAllowances:
|
|
|
54
57
|
"amount": amount,
|
|
55
58
|
"no_cap": no_cap,
|
|
56
59
|
"allow_overage": allow_overage,
|
|
60
|
+
"rollover": rollover,
|
|
57
61
|
"kind": kind,
|
|
58
62
|
"valid_for": valid_for,
|
|
59
63
|
"apply_mode": apply_mode,
|
|
@@ -100,8 +100,9 @@ class CustomerPlan:
|
|
|
100
100
|
plan they are paying for until it runs out, and the new one starts at
|
|
101
101
|
their next boundary. Nothing is pro-rated, because nothing is split.
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
`CycleChangeRequiresResetError
|
|
103
|
+
A weekly plan and a month-based one share no boundary, and between
|
|
104
|
+
them this raises `CycleChangeRequiresResetError`: use `restart` for
|
|
105
|
+
that move. Other cycles land on the coarser plan's boundary.
|
|
105
106
|
"""
|
|
106
107
|
return self.change(customer_id, plan=plan, effective="next_cycle")
|
|
107
108
|
|
|
@@ -121,10 +122,11 @@ class CustomerPlan:
|
|
|
121
122
|
period. Usage already spent still counts, so a downgrade can deny
|
|
122
123
|
until the period ends.
|
|
123
124
|
- `prorate` — each plan's amount weighted by the fraction of the period
|
|
124
|
-
it was held for.
|
|
125
|
-
share no period to weight.
|
|
125
|
+
it was held for.
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
Between plans on different cycles both are refused with
|
|
128
|
+
`CycleChangeRequiresResetError`: there is no period to keep, so use
|
|
129
|
+
`restart`. To have the period itself start over, use `restart` too.
|
|
128
130
|
"""
|
|
129
131
|
return self.change(
|
|
130
132
|
customer_id,
|
|
@@ -174,8 +176,8 @@ class CustomerPlan:
|
|
|
174
176
|
|
|
175
177
|
`None` when the customer holds no plan. That is a default deny rather
|
|
176
178
|
than a failure — they are entitled to nothing — so it is an answer here
|
|
177
|
-
and not a raised `NotFoundError`.
|
|
178
|
-
|
|
179
|
+
and not a raised `NotFoundError`. An id that names no customer answers
|
|
180
|
+
`None` too: the engine reads it as a customer holding no plan.
|
|
179
181
|
"""
|
|
180
182
|
try:
|
|
181
183
|
return self._api.request(
|
|
@@ -100,7 +100,9 @@ class Customers:
|
|
|
100
100
|
return found.data[0] if found.data else None
|
|
101
101
|
|
|
102
102
|
def entitlement(self, id: str) -> CustomerEntitlement | None:
|
|
103
|
-
"""`None` for a customer holding no plan, as `plan.retrieve` answers
|
|
103
|
+
"""`None` for a customer holding no plan, as `plan.retrieve` answers,
|
|
104
|
+
and for an id that names no customer, which the engine reads the same
|
|
105
|
+
way."""
|
|
104
106
|
try:
|
|
105
107
|
return self._api.request(
|
|
106
108
|
"GET",
|
|
@@ -36,6 +36,7 @@ class PlanAllowances:
|
|
|
36
36
|
amount: int | NotGiven = NOT_GIVEN,
|
|
37
37
|
no_cap: bool | NotGiven = NOT_GIVEN,
|
|
38
38
|
allow_overage: bool | NotGiven = NOT_GIVEN,
|
|
39
|
+
rollover: bool | NotGiven = NOT_GIVEN,
|
|
39
40
|
kind: PlanAllowanceKind | NotGiven = NOT_GIVEN,
|
|
40
41
|
valid_for: ValidityParam | Validity | NotGiven = NOT_GIVEN,
|
|
41
42
|
apply_mode: ApplyMode | NotGiven = NOT_GIVEN,
|
|
@@ -45,9 +46,11 @@ class PlanAllowances:
|
|
|
45
46
|
cycle. Give exactly one of `amount` or `no_cap`.
|
|
46
47
|
|
|
47
48
|
`allow_overage` is a soft cap: past `amount`, admit rather than refuse.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
`rollover` carries what a period leaves of `amount` into the next one,
|
|
50
|
+
which spends it first. Both default to `False` and are refused beside
|
|
51
|
+
`no_cap` and on `one_time`. `kind` defaults to `recurring`. `valid_for`
|
|
52
|
+
is `one_time` only. `apply_mode` defaults to `next_cycle`, except
|
|
53
|
+
`one_time` which is always immediate.
|
|
51
54
|
"""
|
|
52
55
|
return self._api.request(
|
|
53
56
|
"POST",
|
|
@@ -58,6 +61,7 @@ class PlanAllowances:
|
|
|
58
61
|
"amount": amount,
|
|
59
62
|
"no_cap": no_cap,
|
|
60
63
|
"allow_overage": allow_overage,
|
|
64
|
+
"rollover": rollover,
|
|
61
65
|
"kind": kind,
|
|
62
66
|
"valid_for": valid_for,
|
|
63
67
|
"apply_mode": apply_mode,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.2"
|
|
@@ -162,11 +162,11 @@ class ReservationMismatchError(InvalidRequestError):
|
|
|
162
162
|
class CycleChangeRequiresResetError(InvalidRequestError):
|
|
163
163
|
"""
|
|
164
164
|
`422 cycle_change_requires_reset`: the two plans measure different cycles,
|
|
165
|
-
so
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
so the change has no period to keep. An immediate change between them is
|
|
166
|
+
refused unless it resets, and a `next_cycle` change is refused between a
|
|
167
|
+
weekly plan and a month-based one, which share no boundary to wait for.
|
|
168
|
+
The move is `customers.plan.restart`, which closes the current period and
|
|
169
|
+
opens a fresh one on the new cycle. The engine's `message` says as much.
|
|
170
170
|
|
|
171
171
|
An `InvalidRequestError` still, so an existing `except` keeps working.
|
|
172
172
|
"""
|
|
@@ -190,9 +190,10 @@ class CheckResult(Model):
|
|
|
190
190
|
available: int | None
|
|
191
191
|
"""
|
|
192
192
|
The whole capacity: the plan's unused entitlement for the current period,
|
|
193
|
-
floored at 0, plus
|
|
194
|
-
is not a number. Under a soft
|
|
195
|
-
amount, and running out of it
|
|
193
|
+
floored at 0, plus what is left of a rollover carry, plus every open
|
|
194
|
+
grant. `None` under `no_cap`, where capacity is not a number. Under a soft
|
|
195
|
+
cap it is what is left of the included amount, and running out of it
|
|
196
|
+
refuses nothing.
|
|
196
197
|
|
|
197
198
|
One figure and no breakdown: `check` answers from a single cached integer,
|
|
198
199
|
and itemising where the capacity came from would cost it that. For the
|
|
@@ -353,6 +354,9 @@ class PlanAllowance(Model):
|
|
|
353
354
|
allow_overage: bool
|
|
354
355
|
"""A soft cap: once `amount` is used up, `check` and `reserve` admit rather
|
|
355
356
|
than refuse, and the usage past it is overage."""
|
|
357
|
+
rollover: bool
|
|
358
|
+
"""What a period leaves unused of `amount` carries into the next, which
|
|
359
|
+
spends it first. The carry lasts that one period."""
|
|
356
360
|
kind: PlanAllowanceKind
|
|
357
361
|
valid_for: Validity | None
|
|
358
362
|
"""`one_time` only; `None` is a grant that never expires."""
|
|
@@ -415,14 +419,22 @@ class _MeterFigures(Model):
|
|
|
415
419
|
no_cap: bool
|
|
416
420
|
allow_overage: bool
|
|
417
421
|
"""The version in effect's; `False` while `awaiting`."""
|
|
422
|
+
rollover: bool
|
|
423
|
+
"""The version in effect's; `False` while `awaiting`."""
|
|
418
424
|
used: int
|
|
419
425
|
"""What counts toward `amount`."""
|
|
420
426
|
total: int
|
|
421
|
-
"""`used`, plus what grants paid for."""
|
|
427
|
+
"""`used`, plus what the carry and grants paid for."""
|
|
422
428
|
remaining: int | None
|
|
423
429
|
overage_used: int | None
|
|
424
430
|
"""`max(used - amount, 0)`, under a hard cap too. `None` where `remaining`
|
|
425
431
|
is."""
|
|
432
|
+
carried_in: int | None
|
|
433
|
+
"""What the period before left of its amount, when both versions roll
|
|
434
|
+
over; `0` otherwise. `None` where `remaining` is."""
|
|
435
|
+
carried_used: int | None
|
|
436
|
+
"""What this period has spent of `carried_in`, which goes first and is not
|
|
437
|
+
in `used`. `None` where `remaining` is."""
|
|
426
438
|
|
|
427
439
|
|
|
428
440
|
class MeterEntitlement(_MeterFigures):
|
|
@@ -449,7 +461,11 @@ class CustomerEntitlement(Model):
|
|
|
449
461
|
|
|
450
462
|
class ClosedPeriodMeter(_MeterFigures):
|
|
451
463
|
"""The entitlement meter at the period's last instant, less what describes
|
|
452
|
-
now. `total - used` is what grants absorbed."""
|
|
464
|
+
now. `total - used - carried_used` is what grants absorbed."""
|
|
465
|
+
|
|
466
|
+
carried_out: int | None
|
|
467
|
+
"""What it passed on: `max(amount - used, 0)` under a version that rolled
|
|
468
|
+
over, `0` otherwise and after a reset. `None` where `remaining` is."""
|
|
453
469
|
|
|
454
470
|
|
|
455
471
|
class ClosedPeriod(Model):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|