nomba-python 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,111 @@
1
+ """
2
+ Generic cursor-pagination helpers.
3
+
4
+ Nomba's own API is cursor-paginated for list-style endpoints (it returns a
5
+ `cursor` field you feed back in as the `cursor` query/body param for the next
6
+ page). These helpers just drive that loop for you — they do not introduce a
7
+ second, separate pagination scheme.
8
+
9
+ Endpoints confirmed paginated by Nomba (per their OpenAPI spec — each returns
10
+ `results` + `cursor`, either at the top level or nested under `data`):
11
+ - accounts.list_all_accounts
12
+ - accounts.fetch_terminals_assigned_to_an_account
13
+ - accounts.fetch_terminals_assigned_to_the_parent_account
14
+ - virtual_accounts.filter_virtual_accounts
15
+ - transactions.fetch_credit_debit_transactions_on_a_sub_account
16
+ - transactions.fetch_credit_debit_transactions_on_the_parent_account
17
+ - transactions.fetch_transactions_on_a_sub_account
18
+ - transactions.filter_account_transactions
19
+ - transactions.fetch_transactions_on_the_parent_account
20
+ - transactions.filter_parent_account_transactions
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from typing import Any, AsyncIterator, Callable, Iterator
25
+
26
+
27
+ def _unwrap(response: dict[str, Any]) -> dict[str, Any]:
28
+ """Nomba wraps some list responses in {code, description, data: {...}}
29
+ and returns others unwrapped as {results, cursor} directly."""
30
+ if "results" in response or "cursor" in response:
31
+ return response
32
+ data = response.get("data")
33
+ if isinstance(data, dict):
34
+ return data
35
+ return response
36
+
37
+
38
+ def paginate(
39
+ method: Callable[..., dict[str, Any]],
40
+ *,
41
+ limit: int | None = None,
42
+ **kwargs: Any,
43
+ ) -> Iterator[dict[str, Any]]:
44
+ """
45
+ Iterate over every item across all pages of a paginated Nomba endpoint.
46
+
47
+ Example:
48
+ from nomba import Nomba
49
+ from nomba.pagination import paginate
50
+
51
+ nomba = Nomba(...)
52
+ for account in paginate(nomba.accounts.list_all_accounts, limit=50):
53
+ print(account["accountRef"])
54
+
55
+ Works with any bound resource method that accepts a `cursor` kwarg and
56
+ returns a `results` list + `cursor` string (see module docstring for the
57
+ confirmed list of such endpoints).
58
+ """
59
+ cursor: str | None = None
60
+ while True:
61
+ call_kwargs = dict(kwargs)
62
+ if limit is not None:
63
+ call_kwargs["limit"] = limit
64
+ if cursor is not None:
65
+ call_kwargs["cursor"] = cursor
66
+
67
+ response = method(**call_kwargs)
68
+ page = _unwrap(response)
69
+ results = page.get("results") or []
70
+ for item in results:
71
+ yield item
72
+
73
+ cursor = page.get("cursor") or None
74
+ if not cursor or not results:
75
+ break
76
+
77
+
78
+ async def apaginate(
79
+ method: Callable[..., Any],
80
+ *,
81
+ limit: int | None = None,
82
+ **kwargs: Any,
83
+ ) -> AsyncIterator[dict[str, Any]]:
84
+ """
85
+ Async version of `paginate`. `method` must be an async resource method.
86
+
87
+ Example:
88
+ from nomba import AsyncNomba
89
+ from nomba.pagination import apaginate
90
+
91
+ async with AsyncNomba(...) as nomba:
92
+ async for account in apaginate(nomba.accounts.list_all_accounts, limit=50):
93
+ print(account["accountRef"])
94
+ """
95
+ cursor: str | None = None
96
+ while True:
97
+ call_kwargs = dict(kwargs)
98
+ if limit is not None:
99
+ call_kwargs["limit"] = limit
100
+ if cursor is not None:
101
+ call_kwargs["cursor"] = cursor
102
+
103
+ response = await method(**call_kwargs)
104
+ page = _unwrap(response)
105
+ results = page.get("results") or []
106
+ for item in results:
107
+ yield item
108
+
109
+ cursor = page.get("cursor") or None
110
+ if not cursor or not results:
111
+ break
nomba_python/py.typed ADDED
File without changes
@@ -0,0 +1,33 @@
1
+ from .accounts import AsyncAccounts, Accounts
2
+ from .airtime_data import AsyncAirtimeData, AirtimeData
3
+ from .cabletv import AsyncCableTv, CableTv
4
+ from .charge import AsyncCharge, Charge
5
+ from .checkout import AsyncCheckout, Checkout
6
+ from .electricity import AsyncElectricity, Electricity
7
+ from .terminals import AsyncTerminals, Terminals
8
+ from .transactions import AsyncTransactions, Transactions
9
+ from .transfers import AsyncTransfers, Transfers
10
+ from .virtual_accounts import AsyncVirtualAccounts, VirtualAccounts
11
+
12
+ __all__ = [
13
+ "Accounts",
14
+ "AirtimeData",
15
+ "AsyncAccounts",
16
+ "AsyncAirtimeData",
17
+ "AsyncCableTv",
18
+ "AsyncCharge",
19
+ "AsyncCheckout",
20
+ "AsyncElectricity",
21
+ "AsyncTerminals",
22
+ "AsyncTransactions",
23
+ "AsyncTransfers",
24
+ "AsyncVirtualAccounts",
25
+ "CableTv",
26
+ "Charge",
27
+ "Checkout",
28
+ "Electricity",
29
+ "Terminals",
30
+ "Transactions",
31
+ "Transfers",
32
+ "VirtualAccounts",
33
+ ]
@@ -0,0 +1,379 @@
1
+ # This file is auto-generated from Nomba's OpenAPI spec. Do not edit by hand;
2
+ # regenerate via scripts/generate_resources.py instead.
3
+ from __future__ import annotations
4
+
5
+ from .. import models as _models
6
+ from ..http import AsyncNombaClient, NombaClient
7
+ from ..validation import validate_body
8
+
9
+
10
+ class Accounts:
11
+ """Sync resource methods for the Accounts group."""
12
+
13
+ def __init__(self, client: NombaClient) -> None:
14
+ self._client = client
15
+
16
+ def list_all_accounts(self, *, limit: str | None = None, cursor: str | None = None, **extra: object) -> _models.ListAllAccountsResponse:
17
+ """
18
+ List all sub accounts
19
+
20
+ You can use this endpoints to fetch all the accounts tied to a business. Accounts are sorted by date, with the most recently-created account appearing first.
21
+ """
22
+ path = "/v1/accounts"
23
+ params: dict[str, object] = {}
24
+ if limit is not None:
25
+ params["limit"] = limit
26
+ if cursor is not None:
27
+ params["cursor"] = cursor
28
+ return self._client.get(path, params=params) # type: ignore[return-value]
29
+
30
+ def create_a_sub_account(self, account_ref, phone_number, email, bvn, pin, account_name, currency, *, callback_url: object | None = None, expiry_date: object | None = None, **extra: object) -> _models.CreateASubAccountResponse:
31
+ """
32
+ Create a sub account
33
+
34
+ You can use this endpoint to create a sub account that is part of your business.
35
+
36
+ Body fields:
37
+ accountRef (required): Account reference
38
+ phoneNumber (required): Phone number
39
+ email (required): Email address
40
+ bvn (required): Bank Verification Number (BVN)
41
+ pin (required): Personal Identification Number (PIN)
42
+ accountName (required): Account holder's name
43
+ currency (required): Currency code
44
+ callbackUrl: Callback url
45
+ expiryDate: Expiry date
46
+ """
47
+ path = "/v1/accounts"
48
+ params = None
49
+ body: dict[str, object] = {}
50
+ body["accountRef"] = account_ref
51
+ body["phoneNumber"] = phone_number
52
+ body["email"] = email
53
+ body["bvn"] = bvn
54
+ body["pin"] = pin
55
+ body["accountName"] = account_name
56
+ body["currency"] = currency
57
+ if callback_url is not None:
58
+ body["callbackUrl"] = callback_url
59
+ if expiry_date is not None:
60
+ body["expiryDate"] = expiry_date
61
+ body.update(extra)
62
+ validate_body("post", "/v1/accounts", body)
63
+ return self._client.post(path, json=body, params=params) # type: ignore[return-value]
64
+
65
+ def fetch_account_details(self, *, account_id: str | None = None, account_ref: str | None = None, **extra: object) -> _models.FetchAccountDetailsResponse:
66
+ """
67
+ Fetch details of a sub account
68
+
69
+ You can use this endpoint to get details of a sub account.
70
+ """
71
+ path = "/v1/accounts/sub-account-details"
72
+ params: dict[str, object] = {}
73
+ if account_id is not None:
74
+ params["accountId"] = account_id
75
+ if account_ref is not None:
76
+ params["accountRef"] = account_ref
77
+ return self._client.get(path, params=params) # type: ignore[return-value]
78
+
79
+ def fetch_parent_account_details(self, **extra: object) -> _models.FetchParentAccountDetailsResponse:
80
+ """
81
+ Fetch parent account details
82
+
83
+ You can use this endpoint to get details of the parent account.
84
+ """
85
+ path = "/v1/accounts/parent"
86
+ params = None
87
+ return self._client.get(path, params=params) # type: ignore[return-value]
88
+
89
+ def fetch_account_balance(self, sub_account_id: str, **extra: object) -> _models.FetchAccountBalanceResponse:
90
+ """
91
+ Fetch sub account balance
92
+
93
+ You can use this endpoint to get the balance of a sub account
94
+ """
95
+ path = f"/v1/accounts/{sub_account_id}/balance"
96
+ params = None
97
+ return self._client.get(path, params=params) # type: ignore[return-value]
98
+
99
+ def fetch_parent_account_balance(self, **extra: object) -> _models.FetchParentAccountBalanceResponse:
100
+ """
101
+ Fetch parent account balance
102
+
103
+ You can use this endpoint to get the balance of the parent account.
104
+ """
105
+ path = "/v1/accounts/balance"
106
+ params = None
107
+ return self._client.get(path, params=params) # type: ignore[return-value]
108
+
109
+ def suspend_an_account(self, sub_account_id: str, **extra: object) -> _models.SuspendAnAccountResponse:
110
+ """
111
+ Suspend a sub account
112
+
113
+ You can use this endpoint to suspend a sub account.
114
+ """
115
+ path = f"/v1/accounts/suspend/{sub_account_id}"
116
+ params = None
117
+ return self._client.put(path, params=params) # type: ignore[return-value]
118
+
119
+ def reactivate_a_sub_account(self, sub_account_id: str, **extra: object) -> _models.ReactivateASubAccountResponse:
120
+ """
121
+ Reactivate a sub account
122
+
123
+ You can use this endpoint to reactivate a sub account
124
+ """
125
+ path = f"/v1/accounts/reactivate/{sub_account_id}"
126
+ params = None
127
+ return self._client.put(path, params=params) # type: ignore[return-value]
128
+
129
+ def fetch_terminals_assigned_to_an_account(self, sub_account_id: str, *, limit: str | None = None, cursor: str | None = None, terminal_id: str | None = None, serial_number: str | None = None, terminal_label: str | None = None, merchant_name: str | None = None, **extra: object) -> _models.FetchTerminalsAssignedToAnAccountResponse:
130
+ """
131
+ Fetch terminals assigned to a sub account
132
+
133
+ You can use this endpoint to fetch terminals linked to a sub account
134
+ """
135
+ path = f"/v1/accounts/{sub_account_id}/terminals"
136
+ params: dict[str, object] = {}
137
+ if limit is not None:
138
+ params["limit"] = limit
139
+ if cursor is not None:
140
+ params["cursor"] = cursor
141
+ if terminal_id is not None:
142
+ params["terminalId"] = terminal_id
143
+ if serial_number is not None:
144
+ params["serialNumber"] = serial_number
145
+ if terminal_label is not None:
146
+ params["terminalLabel"] = terminal_label
147
+ if merchant_name is not None:
148
+ params["merchantName"] = merchant_name
149
+ return self._client.get(path, params=params) # type: ignore[return-value]
150
+
151
+ def fetch_terminals_assigned_to_the_parent_account(self, *, limit: str | None = None, cursor: str | None = None, terminal_id: str | None = None, serial_number: str | None = None, terminal_label: str | None = None, merchant_name: str | None = None, **extra: object) -> _models.FetchTerminalsAssignedToTheParentAccountResponse:
152
+ """
153
+ Fetch terminals assigned to the parent account
154
+
155
+ You can use this endpoint to fetch terminals linked to the parent account.
156
+ """
157
+ path = "/v1/accounts/terminals"
158
+ params: dict[str, object] = {}
159
+ if limit is not None:
160
+ params["limit"] = limit
161
+ if cursor is not None:
162
+ params["cursor"] = cursor
163
+ if terminal_id is not None:
164
+ params["terminalId"] = terminal_id
165
+ if serial_number is not None:
166
+ params["serialNumber"] = serial_number
167
+ if terminal_label is not None:
168
+ params["terminalLabel"] = terminal_label
169
+ if merchant_name is not None:
170
+ params["merchantName"] = merchant_name
171
+ return self._client.get(path, params=params) # type: ignore[return-value]
172
+
173
+ def update_access_to_account(self, sub_account_id: str, grant_type, client_id, role, **extra: object) -> _models.UpdateAccessToAccountResponse:
174
+ """
175
+ Update access to account
176
+
177
+ You can use this endpoint to update the access of an api client within an account.
178
+
179
+ Body fields:
180
+ grantType (required): The action to perform on the access
181
+ clientId (required): The client to grant/deny access to the account
182
+ role (required): The role to assume within the account
183
+ """
184
+ path = f"/v1/accounts/{sub_account_id}/access"
185
+ params = None
186
+ body: dict[str, object] = {}
187
+ body["grantType"] = grant_type
188
+ body["clientId"] = client_id
189
+ body["role"] = role
190
+ body.update(extra)
191
+ validate_body("put", "/v1/accounts/{subAccountId}/access", body)
192
+ return self._client.put(path, json=body, params=params) # type: ignore[return-value]
193
+
194
+
195
+
196
+ class AsyncAccounts:
197
+ """Async resource methods for the Accounts group."""
198
+
199
+ def __init__(self, client: AsyncNombaClient) -> None:
200
+ self._client = client
201
+
202
+ async def list_all_accounts(self, *, limit: str | None = None, cursor: str | None = None, **extra: object) -> _models.ListAllAccountsResponse:
203
+ """
204
+ List all sub accounts
205
+
206
+ You can use this endpoints to fetch all the accounts tied to a business. Accounts are sorted by date, with the most recently-created account appearing first.
207
+ """
208
+ path = "/v1/accounts"
209
+ params: dict[str, object] = {}
210
+ if limit is not None:
211
+ params["limit"] = limit
212
+ if cursor is not None:
213
+ params["cursor"] = cursor
214
+ return await self._client.get(path, params=params) # type: ignore[return-value]
215
+
216
+ async def create_a_sub_account(self, account_ref, phone_number, email, bvn, pin, account_name, currency, *, callback_url: object | None = None, expiry_date: object | None = None, **extra: object) -> _models.CreateASubAccountResponse:
217
+ """
218
+ Create a sub account
219
+
220
+ You can use this endpoint to create a sub account that is part of your business.
221
+
222
+ Body fields:
223
+ accountRef (required): Account reference
224
+ phoneNumber (required): Phone number
225
+ email (required): Email address
226
+ bvn (required): Bank Verification Number (BVN)
227
+ pin (required): Personal Identification Number (PIN)
228
+ accountName (required): Account holder's name
229
+ currency (required): Currency code
230
+ callbackUrl: Callback url
231
+ expiryDate: Expiry date
232
+ """
233
+ path = "/v1/accounts"
234
+ params = None
235
+ body: dict[str, object] = {}
236
+ body["accountRef"] = account_ref
237
+ body["phoneNumber"] = phone_number
238
+ body["email"] = email
239
+ body["bvn"] = bvn
240
+ body["pin"] = pin
241
+ body["accountName"] = account_name
242
+ body["currency"] = currency
243
+ if callback_url is not None:
244
+ body["callbackUrl"] = callback_url
245
+ if expiry_date is not None:
246
+ body["expiryDate"] = expiry_date
247
+ body.update(extra)
248
+ validate_body("post", "/v1/accounts", body)
249
+ return await self._client.post(path, json=body, params=params) # type: ignore[return-value]
250
+
251
+ async def fetch_account_details(self, *, account_id: str | None = None, account_ref: str | None = None, **extra: object) -> _models.FetchAccountDetailsResponse:
252
+ """
253
+ Fetch details of a sub account
254
+
255
+ You can use this endpoint to get details of a sub account.
256
+ """
257
+ path = "/v1/accounts/sub-account-details"
258
+ params: dict[str, object] = {}
259
+ if account_id is not None:
260
+ params["accountId"] = account_id
261
+ if account_ref is not None:
262
+ params["accountRef"] = account_ref
263
+ return await self._client.get(path, params=params) # type: ignore[return-value]
264
+
265
+ async def fetch_parent_account_details(self, **extra: object) -> _models.FetchParentAccountDetailsResponse:
266
+ """
267
+ Fetch parent account details
268
+
269
+ You can use this endpoint to get details of the parent account.
270
+ """
271
+ path = "/v1/accounts/parent"
272
+ params = None
273
+ return await self._client.get(path, params=params) # type: ignore[return-value]
274
+
275
+ async def fetch_account_balance(self, sub_account_id: str, **extra: object) -> _models.FetchAccountBalanceResponse:
276
+ """
277
+ Fetch sub account balance
278
+
279
+ You can use this endpoint to get the balance of a sub account
280
+ """
281
+ path = f"/v1/accounts/{sub_account_id}/balance"
282
+ params = None
283
+ return await self._client.get(path, params=params) # type: ignore[return-value]
284
+
285
+ async def fetch_parent_account_balance(self, **extra: object) -> _models.FetchParentAccountBalanceResponse:
286
+ """
287
+ Fetch parent account balance
288
+
289
+ You can use this endpoint to get the balance of the parent account.
290
+ """
291
+ path = "/v1/accounts/balance"
292
+ params = None
293
+ return await self._client.get(path, params=params) # type: ignore[return-value]
294
+
295
+ async def suspend_an_account(self, sub_account_id: str, **extra: object) -> _models.SuspendAnAccountResponse:
296
+ """
297
+ Suspend a sub account
298
+
299
+ You can use this endpoint to suspend a sub account.
300
+ """
301
+ path = f"/v1/accounts/suspend/{sub_account_id}"
302
+ params = None
303
+ return await self._client.put(path, params=params) # type: ignore[return-value]
304
+
305
+ async def reactivate_a_sub_account(self, sub_account_id: str, **extra: object) -> _models.ReactivateASubAccountResponse:
306
+ """
307
+ Reactivate a sub account
308
+
309
+ You can use this endpoint to reactivate a sub account
310
+ """
311
+ path = f"/v1/accounts/reactivate/{sub_account_id}"
312
+ params = None
313
+ return await self._client.put(path, params=params) # type: ignore[return-value]
314
+
315
+ async def fetch_terminals_assigned_to_an_account(self, sub_account_id: str, *, limit: str | None = None, cursor: str | None = None, terminal_id: str | None = None, serial_number: str | None = None, terminal_label: str | None = None, merchant_name: str | None = None, **extra: object) -> _models.FetchTerminalsAssignedToAnAccountResponse:
316
+ """
317
+ Fetch terminals assigned to a sub account
318
+
319
+ You can use this endpoint to fetch terminals linked to a sub account
320
+ """
321
+ path = f"/v1/accounts/{sub_account_id}/terminals"
322
+ params: dict[str, object] = {}
323
+ if limit is not None:
324
+ params["limit"] = limit
325
+ if cursor is not None:
326
+ params["cursor"] = cursor
327
+ if terminal_id is not None:
328
+ params["terminalId"] = terminal_id
329
+ if serial_number is not None:
330
+ params["serialNumber"] = serial_number
331
+ if terminal_label is not None:
332
+ params["terminalLabel"] = terminal_label
333
+ if merchant_name is not None:
334
+ params["merchantName"] = merchant_name
335
+ return await self._client.get(path, params=params) # type: ignore[return-value]
336
+
337
+ async def fetch_terminals_assigned_to_the_parent_account(self, *, limit: str | None = None, cursor: str | None = None, terminal_id: str | None = None, serial_number: str | None = None, terminal_label: str | None = None, merchant_name: str | None = None, **extra: object) -> _models.FetchTerminalsAssignedToTheParentAccountResponse:
338
+ """
339
+ Fetch terminals assigned to the parent account
340
+
341
+ You can use this endpoint to fetch terminals linked to the parent account.
342
+ """
343
+ path = "/v1/accounts/terminals"
344
+ params: dict[str, object] = {}
345
+ if limit is not None:
346
+ params["limit"] = limit
347
+ if cursor is not None:
348
+ params["cursor"] = cursor
349
+ if terminal_id is not None:
350
+ params["terminalId"] = terminal_id
351
+ if serial_number is not None:
352
+ params["serialNumber"] = serial_number
353
+ if terminal_label is not None:
354
+ params["terminalLabel"] = terminal_label
355
+ if merchant_name is not None:
356
+ params["merchantName"] = merchant_name
357
+ return await self._client.get(path, params=params) # type: ignore[return-value]
358
+
359
+ async def update_access_to_account(self, sub_account_id: str, grant_type, client_id, role, **extra: object) -> _models.UpdateAccessToAccountResponse:
360
+ """
361
+ Update access to account
362
+
363
+ You can use this endpoint to update the access of an api client within an account.
364
+
365
+ Body fields:
366
+ grantType (required): The action to perform on the access
367
+ clientId (required): The client to grant/deny access to the account
368
+ role (required): The role to assume within the account
369
+ """
370
+ path = f"/v1/accounts/{sub_account_id}/access"
371
+ params = None
372
+ body: dict[str, object] = {}
373
+ body["grantType"] = grant_type
374
+ body["clientId"] = client_id
375
+ body["role"] = role
376
+ body.update(extra)
377
+ validate_body("put", "/v1/accounts/{subAccountId}/access", body)
378
+ return await self._client.put(path, json=body, params=params) # type: ignore[return-value]
379
+