meterbase 0.3.0__tar.gz → 0.4.0__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.3.0 → meterbase-0.4.0}/PKG-INFO +29 -3
- {meterbase-0.3.0 → meterbase-0.4.0}/README.md +28 -2
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/customers.py +68 -10
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_http.py +15 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_models.py +12 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/customers.py +68 -10
- meterbase-0.4.0/src/meterbase/_version.py +1 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/types.py +2 -1
- meterbase-0.3.0/src/meterbase/_version.py +0 -1
- {meterbase-0.3.0 → meterbase-0.4.0}/.gitignore +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/LICENSE +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/pyproject.toml +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/__init__.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/__init__.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/_io.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/_transport.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/client.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/__init__.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/customer_allowances.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/customer_periods.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/customer_plan.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/meters.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_async/resources/plans.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_json.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_keys.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_not_given.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/__init__.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/_io.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/_transport.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/client.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/__init__.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/customer_allowances.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/customer_periods.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/customer_plan.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/meters.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_sync/resources/plans.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/_time.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/errors.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/py.typed +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/resources.py +0 -0
- {meterbase-0.3.0 → meterbase-0.4.0}/src/meterbase/webhooks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: meterbase
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -278,7 +278,7 @@ id and gives them a first plan, and running it again changes nothing:
|
|
|
278
278
|
ensured = meterbase.customers.ensure(
|
|
279
279
|
external_id="acct_1",
|
|
280
280
|
name="Acme", # applied only when it creates the customer
|
|
281
|
-
plan="free", # a plan key
|
|
281
|
+
plan="free", # a plan's key or id
|
|
282
282
|
)
|
|
283
283
|
ensured.created, ensured.plan_result, ensured.customer.plan
|
|
284
284
|
```
|
|
@@ -303,7 +303,6 @@ customer = meterbase.customers.create(
|
|
|
303
303
|
)
|
|
304
304
|
|
|
305
305
|
found = meterbase.customers.retrieve_by_external_id("acct_1") # or None
|
|
306
|
-
everyone = meterbase.customers.list(include_deleted=True).data
|
|
307
306
|
|
|
308
307
|
meterbase.customers.update(customer.id, name="Acme Inc")
|
|
309
308
|
meterbase.customers.delete(customer.id) # soft delete, idempotent
|
|
@@ -328,6 +327,27 @@ embed is which plan, and `customers.plan.retrieve` is where instructions are
|
|
|
328
327
|
identified. `None` means they hold no plan, which is a default deny — a
|
|
329
328
|
customer with no plan and no grant is denied.
|
|
330
329
|
|
|
330
|
+
**Lists come a page at a time.** `customers.list` answers one page, newest
|
|
331
|
+
first: `limit` customers, 100 unless you set it and at most 1,000. `list_all`
|
|
332
|
+
follows the pages for you, fetching each one as the loop reaches it:
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
page = meterbase.customers.list(plan="pro", limit=1000)
|
|
336
|
+
page.data # the newest 1,000 customers on pro
|
|
337
|
+
page.has_more # True when another page follows the last of them
|
|
338
|
+
|
|
339
|
+
for customer in meterbase.customers.list_all(has_plan=False):
|
|
340
|
+
... # every customer holding no plan
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
On the async client it is `async for customer in meterbase.customers.list_all()`.
|
|
344
|
+
`plan`, a key or an id, and `has_plan` filter on the plan in force now; a
|
|
345
|
+
pending change counts once it lands. `include_deleted=True` adds deleted
|
|
346
|
+
customers. To page by hand, pass the last `id` as `starting_after` while
|
|
347
|
+
`has_more` is `True`. A walk reads no customer twice, and one created while it
|
|
348
|
+
runs is not in it. Both raise before sending when `plan` or `starting_after` is
|
|
349
|
+
`None`, which would otherwise widen the list.
|
|
350
|
+
|
|
331
351
|
`customers.list` stays lean and does not embed, along with `create`, `update`
|
|
332
352
|
and `delete`; the same read would run per row for an answer most callers of a
|
|
333
353
|
list do not want.
|
|
@@ -994,6 +1014,12 @@ Pre-1.0, the minor version is where breaking changes land — deliberately, so a
|
|
|
994
1014
|
change that will not run against your code cannot arrive as a patch. The SDK
|
|
995
1015
|
follows the engine's API, as the TypeScript SDK (`mbase-sdk`) does.
|
|
996
1016
|
|
|
1017
|
+
**0.4.0** pages the customer list, and adds `customers.list_all` and the
|
|
1018
|
+
`limit`, `starting_after`, `plan` and `has_plan` parameters. One change can break
|
|
1019
|
+
code: `customers.list()` answers one page of 100, newest first, where it used to
|
|
1020
|
+
answer every customer. Iterate `list_all` to read them all. The engine pages the
|
|
1021
|
+
same way, so 0.3.0 and earlier now read only the newest 100.
|
|
1022
|
+
|
|
997
1023
|
**0.3.0** adds an `idempotency_key` on grants and plan changes, which also makes
|
|
998
1024
|
them safe to retry, `replayed` on their answers, and `IdempotencyKeyReusedError`.
|
|
999
1025
|
Nothing in it breaks code written for 0.2.0.
|
|
@@ -251,7 +251,7 @@ id and gives them a first plan, and running it again changes nothing:
|
|
|
251
251
|
ensured = meterbase.customers.ensure(
|
|
252
252
|
external_id="acct_1",
|
|
253
253
|
name="Acme", # applied only when it creates the customer
|
|
254
|
-
plan="free", # a plan key
|
|
254
|
+
plan="free", # a plan's key or id
|
|
255
255
|
)
|
|
256
256
|
ensured.created, ensured.plan_result, ensured.customer.plan
|
|
257
257
|
```
|
|
@@ -276,7 +276,6 @@ customer = meterbase.customers.create(
|
|
|
276
276
|
)
|
|
277
277
|
|
|
278
278
|
found = meterbase.customers.retrieve_by_external_id("acct_1") # or None
|
|
279
|
-
everyone = meterbase.customers.list(include_deleted=True).data
|
|
280
279
|
|
|
281
280
|
meterbase.customers.update(customer.id, name="Acme Inc")
|
|
282
281
|
meterbase.customers.delete(customer.id) # soft delete, idempotent
|
|
@@ -301,6 +300,27 @@ embed is which plan, and `customers.plan.retrieve` is where instructions are
|
|
|
301
300
|
identified. `None` means they hold no plan, which is a default deny — a
|
|
302
301
|
customer with no plan and no grant is denied.
|
|
303
302
|
|
|
303
|
+
**Lists come a page at a time.** `customers.list` answers one page, newest
|
|
304
|
+
first: `limit` customers, 100 unless you set it and at most 1,000. `list_all`
|
|
305
|
+
follows the pages for you, fetching each one as the loop reaches it:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
page = meterbase.customers.list(plan="pro", limit=1000)
|
|
309
|
+
page.data # the newest 1,000 customers on pro
|
|
310
|
+
page.has_more # True when another page follows the last of them
|
|
311
|
+
|
|
312
|
+
for customer in meterbase.customers.list_all(has_plan=False):
|
|
313
|
+
... # every customer holding no plan
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
On the async client it is `async for customer in meterbase.customers.list_all()`.
|
|
317
|
+
`plan`, a key or an id, and `has_plan` filter on the plan in force now; a
|
|
318
|
+
pending change counts once it lands. `include_deleted=True` adds deleted
|
|
319
|
+
customers. To page by hand, pass the last `id` as `starting_after` while
|
|
320
|
+
`has_more` is `True`. A walk reads no customer twice, and one created while it
|
|
321
|
+
runs is not in it. Both raise before sending when `plan` or `starting_after` is
|
|
322
|
+
`None`, which would otherwise widen the list.
|
|
323
|
+
|
|
304
324
|
`customers.list` stays lean and does not embed, along with `create`, `update`
|
|
305
325
|
and `delete`; the same read would run per row for an answer most callers of a
|
|
306
326
|
list do not want.
|
|
@@ -967,6 +987,12 @@ Pre-1.0, the minor version is where breaking changes land — deliberately, so a
|
|
|
967
987
|
change that will not run against your code cannot arrive as a patch. The SDK
|
|
968
988
|
follows the engine's API, as the TypeScript SDK (`mbase-sdk`) does.
|
|
969
989
|
|
|
990
|
+
**0.4.0** pages the customer list, and adds `customers.list_all` and the
|
|
991
|
+
`limit`, `starting_after`, `plan` and `has_plan` parameters. One change can break
|
|
992
|
+
code: `customers.list()` answers one page of 100, newest first, where it used to
|
|
993
|
+
answer every customer. Iterate `list_all` to read them all. The engine pages the
|
|
994
|
+
same way, so 0.3.0 and earlier now read only the newest 100.
|
|
995
|
+
|
|
970
996
|
**0.3.0** adds an `idempotency_key` on grants and plan changes, which also makes
|
|
971
997
|
them safe to retry, `replayed` on their answers, and `IdempotencyKeyReusedError`.
|
|
972
998
|
Nothing in it breaks code written for 0.2.0.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import Mapping, Sequence
|
|
3
|
+
from collections.abc import AsyncIterator, Mapping, Sequence
|
|
4
4
|
from typing import Any, overload
|
|
5
5
|
|
|
6
|
-
from ..._http import require_external_id, segment
|
|
7
|
-
from ..._models import
|
|
6
|
+
from ..._http import require_external_id, require_list_filters, segment
|
|
7
|
+
from ..._models import Page, list_of, page_of
|
|
8
8
|
from ..._not_given import NOT_GIVEN, NotGiven
|
|
9
9
|
from ...errors import NotFoundError
|
|
10
10
|
from ...types import (
|
|
@@ -78,7 +78,7 @@ class AsyncCustomers:
|
|
|
78
78
|
happened.
|
|
79
79
|
|
|
80
80
|
`name` and `metadata` apply only to a customer this creates. `plan` is
|
|
81
|
-
a plan key; omit it to leave the plan alone. `cycle_anchor`, RFC 3339,
|
|
81
|
+
a plan's key or id; omit it to leave the plan alone. `cycle_anchor`, RFC 3339,
|
|
82
82
|
only with `plan` and never in the future, is used only when this call
|
|
83
83
|
assigns the plan. `mint_one_time=False` writes the plan's one-time
|
|
84
84
|
grants already spent, for an imported customer whose remaining bonus
|
|
@@ -105,17 +105,75 @@ class AsyncCustomers:
|
|
|
105
105
|
)
|
|
106
106
|
|
|
107
107
|
async def list(
|
|
108
|
-
self,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
self,
|
|
109
|
+
*,
|
|
110
|
+
limit: int | NotGiven = NOT_GIVEN,
|
|
111
|
+
starting_after: str | NotGiven = NOT_GIVEN,
|
|
112
|
+
plan: str | NotGiven = NOT_GIVEN,
|
|
113
|
+
has_plan: bool | NotGiven = NOT_GIVEN,
|
|
114
|
+
include_deleted: bool | NotGiven = NOT_GIVEN,
|
|
115
|
+
) -> Page[Customer]:
|
|
116
|
+
"""
|
|
117
|
+
One page, newest first: `limit` customers, 100 unless set and at most
|
|
118
|
+
1,000. Pass the last `id` as `starting_after` for the next page, while
|
|
119
|
+
`has_more` is `True`; `list_all` does that for you.
|
|
120
|
+
|
|
121
|
+
`plan`, a key or an id, and `has_plan` filter on the plan in force now;
|
|
122
|
+
a pending change counts once it lands. Lean: a page does not embed the
|
|
123
|
+
plan. Read one customer for that.
|
|
124
|
+
|
|
125
|
+
Raises `MeterbaseError` before sending when `plan` or `starting_after`
|
|
126
|
+
is `None`.
|
|
127
|
+
"""
|
|
128
|
+
require_list_filters("list", plan, starting_after)
|
|
129
|
+
|
|
112
130
|
return await self._api.request(
|
|
113
131
|
"GET",
|
|
114
132
|
"/v1/customers",
|
|
115
|
-
|
|
116
|
-
query={
|
|
133
|
+
page_of(Customer),
|
|
134
|
+
query={
|
|
135
|
+
"limit": limit,
|
|
136
|
+
"starting_after": starting_after,
|
|
137
|
+
"plan": plan,
|
|
138
|
+
"has_plan": has_plan,
|
|
139
|
+
"include_deleted": include_deleted,
|
|
140
|
+
},
|
|
117
141
|
)
|
|
118
142
|
|
|
143
|
+
async def list_all(
|
|
144
|
+
self,
|
|
145
|
+
*,
|
|
146
|
+
limit: int | NotGiven = NOT_GIVEN,
|
|
147
|
+
starting_after: str | NotGiven = NOT_GIVEN,
|
|
148
|
+
plan: str | NotGiven = NOT_GIVEN,
|
|
149
|
+
has_plan: bool | NotGiven = NOT_GIVEN,
|
|
150
|
+
include_deleted: bool | NotGiven = NOT_GIVEN,
|
|
151
|
+
) -> AsyncIterator[Customer]:
|
|
152
|
+
"""
|
|
153
|
+
Every customer, newest first, from `starting_after` on when it is
|
|
154
|
+
given, one page of `limit` at a time as the loop asks for more:
|
|
155
|
+
`async for customer in client.customers.list_all(): ...`.
|
|
156
|
+
|
|
157
|
+
A customer created during the walk is not in it, and none is read
|
|
158
|
+
twice.
|
|
159
|
+
"""
|
|
160
|
+
require_list_filters("list_all", plan, starting_after)
|
|
161
|
+
|
|
162
|
+
while True:
|
|
163
|
+
page = await self.list(
|
|
164
|
+
limit=limit,
|
|
165
|
+
starting_after=starting_after,
|
|
166
|
+
plan=plan,
|
|
167
|
+
has_plan=has_plan,
|
|
168
|
+
include_deleted=include_deleted,
|
|
169
|
+
)
|
|
170
|
+
for customer in page.data:
|
|
171
|
+
starting_after = customer.id
|
|
172
|
+
yield customer
|
|
173
|
+
|
|
174
|
+
if not page.has_more or not page.data:
|
|
175
|
+
return
|
|
176
|
+
|
|
119
177
|
async def retrieve(self, id: str) -> CustomerWithPlan:
|
|
120
178
|
"""
|
|
121
179
|
One customer, with the plan they hold. `plan` is the one in force now
|
|
@@ -86,6 +86,21 @@ def require_external_id(method: str, external_id: object) -> None:
|
|
|
86
86
|
)
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
def require_list_filters(method: str, plan: object, starting_after: object) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Dropped from the query, a `None` would widen the list: every customer
|
|
92
|
+
rather than one plan's, or the first page again rather than the next.
|
|
93
|
+
"""
|
|
94
|
+
for name, value, instead in (
|
|
95
|
+
("plan", plan, "omit it to list every customer"),
|
|
96
|
+
("starting_after", starting_after, "omit it to start at the newest"),
|
|
97
|
+
):
|
|
98
|
+
if value is None:
|
|
99
|
+
raise MeterbaseError(
|
|
100
|
+
f"customers.{method} was given {name}=None; {instead}."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
89
104
|
def require_key_filter(key: object) -> None:
|
|
90
105
|
"""
|
|
91
106
|
Dropped from the query, a `None` key would list every grant where a
|
|
@@ -111,11 +111,23 @@ class ListResult(Model, Generic[_T]):
|
|
|
111
111
|
]
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
class Page(ListResult[_T]):
|
|
115
|
+
"""One page of a paged list. `has_more` is `True` when another page follows
|
|
116
|
+
the last item in `data`."""
|
|
117
|
+
|
|
118
|
+
has_more: bool
|
|
119
|
+
|
|
120
|
+
|
|
114
121
|
def list_of(item: type[_M]) -> Callable[[Mapping[str, Any]], ListResult[_M]]:
|
|
115
122
|
"""Reads a listing whose rows are `item`."""
|
|
116
123
|
return lambda raw: ListResult(raw, item)
|
|
117
124
|
|
|
118
125
|
|
|
126
|
+
def page_of(item: type[_M]) -> Callable[[Mapping[str, Any]], Page[_M]]:
|
|
127
|
+
"""Reads a page whose rows are `item`."""
|
|
128
|
+
return lambda raw: Page(raw, item)
|
|
129
|
+
|
|
130
|
+
|
|
119
131
|
def construct(cast_to: Callable[[dict[str, Any]], _T], data: Any) -> _T:
|
|
120
132
|
"""Reads a response body as the object a call answers with."""
|
|
121
133
|
if not is_object(data):
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from collections.abc import Mapping, Sequence
|
|
7
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
8
8
|
from typing import Any, overload
|
|
9
9
|
|
|
10
|
-
from ..._http import require_external_id, segment
|
|
11
|
-
from ..._models import
|
|
10
|
+
from ..._http import require_external_id, require_list_filters, segment
|
|
11
|
+
from ..._models import Page, list_of, page_of
|
|
12
12
|
from ..._not_given import NOT_GIVEN, NotGiven
|
|
13
13
|
from ...errors import NotFoundError
|
|
14
14
|
from ...types import (
|
|
@@ -82,7 +82,7 @@ class Customers:
|
|
|
82
82
|
happened.
|
|
83
83
|
|
|
84
84
|
`name` and `metadata` apply only to a customer this creates. `plan` is
|
|
85
|
-
a plan key; omit it to leave the plan alone. `cycle_anchor`, RFC 3339,
|
|
85
|
+
a plan's key or id; omit it to leave the plan alone. `cycle_anchor`, RFC 3339,
|
|
86
86
|
only with `plan` and never in the future, is used only when this call
|
|
87
87
|
assigns the plan. `mint_one_time=False` writes the plan's one-time
|
|
88
88
|
grants already spent, for an imported customer whose remaining bonus
|
|
@@ -109,17 +109,75 @@ class Customers:
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
def list(
|
|
112
|
-
self,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
self,
|
|
113
|
+
*,
|
|
114
|
+
limit: int | NotGiven = NOT_GIVEN,
|
|
115
|
+
starting_after: str | NotGiven = NOT_GIVEN,
|
|
116
|
+
plan: str | NotGiven = NOT_GIVEN,
|
|
117
|
+
has_plan: bool | NotGiven = NOT_GIVEN,
|
|
118
|
+
include_deleted: bool | NotGiven = NOT_GIVEN,
|
|
119
|
+
) -> Page[Customer]:
|
|
120
|
+
"""
|
|
121
|
+
One page, newest first: `limit` customers, 100 unless set and at most
|
|
122
|
+
1,000. Pass the last `id` as `starting_after` for the next page, while
|
|
123
|
+
`has_more` is `True`; `list_all` does that for you.
|
|
124
|
+
|
|
125
|
+
`plan`, a key or an id, and `has_plan` filter on the plan in force now;
|
|
126
|
+
a pending change counts once it lands. Lean: a page does not embed the
|
|
127
|
+
plan. Read one customer for that.
|
|
128
|
+
|
|
129
|
+
Raises `MeterbaseError` before sending when `plan` or `starting_after`
|
|
130
|
+
is `None`.
|
|
131
|
+
"""
|
|
132
|
+
require_list_filters("list", plan, starting_after)
|
|
133
|
+
|
|
116
134
|
return self._api.request(
|
|
117
135
|
"GET",
|
|
118
136
|
"/v1/customers",
|
|
119
|
-
|
|
120
|
-
query={
|
|
137
|
+
page_of(Customer),
|
|
138
|
+
query={
|
|
139
|
+
"limit": limit,
|
|
140
|
+
"starting_after": starting_after,
|
|
141
|
+
"plan": plan,
|
|
142
|
+
"has_plan": has_plan,
|
|
143
|
+
"include_deleted": include_deleted,
|
|
144
|
+
},
|
|
121
145
|
)
|
|
122
146
|
|
|
147
|
+
def list_all(
|
|
148
|
+
self,
|
|
149
|
+
*,
|
|
150
|
+
limit: int | NotGiven = NOT_GIVEN,
|
|
151
|
+
starting_after: str | NotGiven = NOT_GIVEN,
|
|
152
|
+
plan: str | NotGiven = NOT_GIVEN,
|
|
153
|
+
has_plan: bool | NotGiven = NOT_GIVEN,
|
|
154
|
+
include_deleted: bool | NotGiven = NOT_GIVEN,
|
|
155
|
+
) -> Iterator[Customer]:
|
|
156
|
+
"""
|
|
157
|
+
Every customer, newest first, from `starting_after` on when it is
|
|
158
|
+
given, one page of `limit` at a time as the loop asks for more:
|
|
159
|
+
`for customer in client.customers.list_all(): ...`.
|
|
160
|
+
|
|
161
|
+
A customer created during the walk is not in it, and none is read
|
|
162
|
+
twice.
|
|
163
|
+
"""
|
|
164
|
+
require_list_filters("list_all", plan, starting_after)
|
|
165
|
+
|
|
166
|
+
while True:
|
|
167
|
+
page = self.list(
|
|
168
|
+
limit=limit,
|
|
169
|
+
starting_after=starting_after,
|
|
170
|
+
plan=plan,
|
|
171
|
+
has_plan=has_plan,
|
|
172
|
+
include_deleted=include_deleted,
|
|
173
|
+
)
|
|
174
|
+
for customer in page.data:
|
|
175
|
+
starting_after = customer.id
|
|
176
|
+
yield customer
|
|
177
|
+
|
|
178
|
+
if not page.has_more or not page.data:
|
|
179
|
+
return
|
|
180
|
+
|
|
123
181
|
def retrieve(self, id: str) -> CustomerWithPlan:
|
|
124
182
|
"""
|
|
125
183
|
One customer, with the plan they hold. `plan` is the one in force now
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
from typing import Any, Literal, TypedDict
|
|
14
14
|
|
|
15
|
-
from ._models import ListResult, Model
|
|
15
|
+
from ._models import ListResult, Model, Page
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
18
18
|
"Allowance",
|
|
@@ -45,6 +45,7 @@ __all__ = [
|
|
|
45
45
|
"MeterUsage",
|
|
46
46
|
"MeterUsageHistory",
|
|
47
47
|
"Model",
|
|
48
|
+
"Page",
|
|
48
49
|
"Period",
|
|
49
50
|
"Plan",
|
|
50
51
|
"PlanAllowance",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|