smscode 1.0.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,359 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Callable, Mapping
4
+ from typing import Any
5
+
6
+ from smscode.errors import InvalidResponseError
7
+ from smscode.models import (
8
+ Country,
9
+ ExchangeRate,
10
+ Product,
11
+ ProductsPageV1,
12
+ ProductsPageV2,
13
+ ProductV2,
14
+ Service,
15
+ V2Fx,
16
+ parse_v2_fx,
17
+ )
18
+ from smscode.money import parse_money
19
+ from smscode.types import ApiResult, HttpMethod, QueryValue, SortQuery
20
+
21
+ SyncRequest = Callable[
22
+ [HttpMethod, str],
23
+ ApiResult[Any],
24
+ ]
25
+ AsyncRequest = Callable[
26
+ [HttpMethod, str],
27
+ Awaitable[ApiResult[Any]],
28
+ ]
29
+
30
+
31
+ def _products_params(
32
+ *,
33
+ country_id: int | None = None,
34
+ platform_id: int | None = None,
35
+ limit: int | None = None,
36
+ page: int | None = None,
37
+ sort: SortQuery | None = None,
38
+ ) -> dict[str, QueryValue]:
39
+ return {
40
+ "country_id": country_id,
41
+ "platform_id": platform_id,
42
+ "limit": limit,
43
+ "page": page,
44
+ "sort": sort,
45
+ }
46
+
47
+
48
+ def _services_params(*, country_id: int | None = None) -> dict[str, QueryValue]:
49
+ return {"country_id": country_id}
50
+
51
+
52
+ def _exchange_rate_params(*, pair: str | None = None) -> dict[str, QueryValue]:
53
+ return {"pair": pair}
54
+
55
+
56
+ def _decode_v2_products(result: ApiResult[Any]) -> ProductsPageV2:
57
+ meta = result.meta or {}
58
+ fx = parse_v2_fx(meta.get("fx"))
59
+ products: list[Product] = [
60
+ _decode_v2_product(item) for item in result.data if isinstance(item, Mapping)
61
+ ]
62
+ return ProductsPageV2(
63
+ products=products,
64
+ page=_int_or_none(meta.get("page")),
65
+ limit=_int_or_none(meta.get("limit")),
66
+ count=_int_or_none(meta.get("count")),
67
+ fx=fx,
68
+ )
69
+
70
+
71
+ def _decode_v1_products(result: ApiResult[Any]) -> ProductsPageV1:
72
+ meta = result.meta or {}
73
+ products = [_decode_v1_product(item) for item in result.data if isinstance(item, Mapping)]
74
+ return ProductsPageV1(
75
+ products=products,
76
+ page=_int_or_none(meta.get("page")),
77
+ limit=_int_or_none(meta.get("limit")),
78
+ count=_int_or_none(meta.get("count")),
79
+ )
80
+
81
+
82
+ def _decode_v2_product(item: Mapping[str, Any]) -> ProductV2:
83
+ raw = dict(item)
84
+ return ProductV2(
85
+ id=int(item["id"]),
86
+ name=item.get("name") if isinstance(item.get("name"), str) else None,
87
+ country_id=_int_or_none(item.get("country_id")),
88
+ platform_id=_int_or_none(item.get("platform_id")),
89
+ available=int(item["available"]),
90
+ price=parse_money(item.get("price")),
91
+ active=bool(item["active"]),
92
+ catalog_product_id=_int_or_none(item.get("catalog_product_id")),
93
+ raw=raw,
94
+ )
95
+
96
+
97
+ def _decode_v1_product(item: Mapping[str, Any]) -> Product:
98
+ raw = dict(item)
99
+ return Product(
100
+ id=int(item["id"]),
101
+ name=item.get("name") if isinstance(item.get("name"), str) else None,
102
+ country_id=_int_or_none(item.get("country_id")),
103
+ platform_id=_int_or_none(item.get("platform_id")),
104
+ available=int(item["available"]),
105
+ price=item.get("price") if type(item.get("price")) is int else None,
106
+ active=bool(item["active"]),
107
+ catalog_product_id=_int_or_none(item.get("catalog_product_id")),
108
+ raw=raw,
109
+ )
110
+
111
+
112
+ def _decode_country(item: Mapping[str, Any]) -> Country:
113
+ raw = dict(item)
114
+ return Country(
115
+ id=int(item["id"]),
116
+ name=str(item["name"]),
117
+ code=item.get("code") if isinstance(item.get("code"), str) else None,
118
+ dial_code=item.get("dial_code") if isinstance(item.get("dial_code"), str) else None,
119
+ emoji=item.get("emoji") if isinstance(item.get("emoji"), str) else None,
120
+ active=bool(item.get("active", True)),
121
+ raw=raw,
122
+ )
123
+
124
+
125
+ def _decode_service(item: Mapping[str, Any]) -> Service:
126
+ raw = dict(item)
127
+ return Service(
128
+ id=int(item["id"]),
129
+ name=str(item["name"]),
130
+ code=str(item["code"]),
131
+ active=bool(item.get("active", True)),
132
+ raw=raw,
133
+ )
134
+
135
+
136
+ def _decode_countries(data: object) -> list[Country]:
137
+ return (
138
+ [_decode_country(item) for item in data if isinstance(item, Mapping)]
139
+ if isinstance(data, list)
140
+ else []
141
+ )
142
+
143
+
144
+ def _decode_services(data: object) -> list[Service]:
145
+ return (
146
+ [_decode_service(item) for item in data if isinstance(item, Mapping)]
147
+ if isinstance(data, list)
148
+ else []
149
+ )
150
+
151
+
152
+ def _decode_exchange_rate(data: object) -> ExchangeRate:
153
+ if not isinstance(data, Mapping):
154
+ raise InvalidResponseError("The exchange-rate response is malformed.")
155
+ raw = dict(data)
156
+ return ExchangeRate(
157
+ pair=str(data["pair"]),
158
+ rate=data["rate"] if isinstance(data.get("rate"), (int, str)) else str(data.get("rate")),
159
+ base_currency=data.get("base_currency")
160
+ if isinstance(data.get("base_currency"), str)
161
+ else None,
162
+ quote_currency=data.get("quote_currency")
163
+ if isinstance(data.get("quote_currency"), str)
164
+ else None,
165
+ rate_as_of=data.get("rate_as_of") if isinstance(data.get("rate_as_of"), str) else None,
166
+ raw=raw,
167
+ )
168
+
169
+
170
+ def _int_or_none(value: object) -> int | None:
171
+ return value if type(value) is int else None
172
+
173
+
174
+ class V2CatalogResource:
175
+ def __init__(self, request: Any) -> None:
176
+ self._request = request
177
+
178
+ def countries(self) -> list[Country]:
179
+ result = self._request("GET", "/v2/catalog/countries")
180
+ return _decode_countries(result.data)
181
+
182
+ def services(self, *, country_id: int | None = None) -> list[Service]:
183
+ result = self._request(
184
+ "GET",
185
+ "/v2/catalog/services",
186
+ params=_services_params(country_id=country_id),
187
+ )
188
+ return _decode_services(result.data)
189
+
190
+ def products(
191
+ self,
192
+ *,
193
+ country_id: int | None = None,
194
+ platform_id: int | None = None,
195
+ limit: int | None = None,
196
+ page: int | None = None,
197
+ sort: SortQuery | None = None,
198
+ ) -> ProductsPageV2:
199
+ result = self._request(
200
+ "GET",
201
+ "/v2/catalog/products",
202
+ params=_products_params(
203
+ country_id=country_id,
204
+ platform_id=platform_id,
205
+ limit=limit,
206
+ page=page,
207
+ sort=sort,
208
+ ),
209
+ )
210
+ return _decode_v2_products(result)
211
+
212
+ def exchange_rate(self, *, pair: str | None = None) -> V2Fx:
213
+ result = self._request(
214
+ "GET",
215
+ "/v2/catalog/exchange-rate",
216
+ params=_exchange_rate_params(pair=pair),
217
+ )
218
+ return parse_v2_fx(result.data)
219
+
220
+
221
+ class V1CatalogResource:
222
+ def __init__(self, request: Any) -> None:
223
+ self._request = request
224
+
225
+ def countries(self) -> list[Country]:
226
+ result = self._request("GET", "/v1/catalog/countries")
227
+ return _decode_countries(result.data)
228
+
229
+ def services(self, *, country_id: int | None = None) -> list[Service]:
230
+ result = self._request(
231
+ "GET",
232
+ "/v1/catalog/services",
233
+ params=_services_params(country_id=country_id),
234
+ )
235
+ return _decode_services(result.data)
236
+
237
+ def products(
238
+ self,
239
+ *,
240
+ country_id: int | None = None,
241
+ platform_id: int | None = None,
242
+ limit: int | None = None,
243
+ page: int | None = None,
244
+ sort: SortQuery | None = None,
245
+ ) -> ProductsPageV1:
246
+ result = self._request(
247
+ "GET",
248
+ "/v1/catalog/products",
249
+ params=_products_params(
250
+ country_id=country_id,
251
+ platform_id=platform_id,
252
+ limit=limit,
253
+ page=page,
254
+ sort=sort,
255
+ ),
256
+ )
257
+ return _decode_v1_products(result)
258
+
259
+ def exchange_rate(self, *, pair: str | None = None) -> ExchangeRate:
260
+ result = self._request(
261
+ "GET",
262
+ "/v1/catalog/exchange-rate",
263
+ params=_exchange_rate_params(pair=pair),
264
+ )
265
+ return _decode_exchange_rate(result.data)
266
+
267
+
268
+ class AsyncV2CatalogResource:
269
+ def __init__(self, request: Any) -> None:
270
+ self._request = request
271
+
272
+ async def countries(self) -> list[Country]:
273
+ result = await self._request("GET", "/v2/catalog/countries")
274
+ return _decode_countries(result.data)
275
+
276
+ async def services(self, *, country_id: int | None = None) -> list[Service]:
277
+ result = await self._request(
278
+ "GET",
279
+ "/v2/catalog/services",
280
+ params=_services_params(country_id=country_id),
281
+ )
282
+ return _decode_services(result.data)
283
+
284
+ async def products(
285
+ self,
286
+ *,
287
+ country_id: int | None = None,
288
+ platform_id: int | None = None,
289
+ limit: int | None = None,
290
+ page: int | None = None,
291
+ sort: SortQuery | None = None,
292
+ ) -> ProductsPageV2:
293
+ result = await self._request(
294
+ "GET",
295
+ "/v2/catalog/products",
296
+ params=_products_params(
297
+ country_id=country_id,
298
+ platform_id=platform_id,
299
+ limit=limit,
300
+ page=page,
301
+ sort=sort,
302
+ ),
303
+ )
304
+ return _decode_v2_products(result)
305
+
306
+ async def exchange_rate(self, *, pair: str | None = None) -> V2Fx:
307
+ result = await self._request(
308
+ "GET",
309
+ "/v2/catalog/exchange-rate",
310
+ params=_exchange_rate_params(pair=pair),
311
+ )
312
+ return parse_v2_fx(result.data)
313
+
314
+
315
+ class AsyncV1CatalogResource:
316
+ def __init__(self, request: Any) -> None:
317
+ self._request = request
318
+
319
+ async def countries(self) -> list[Country]:
320
+ result = await self._request("GET", "/v1/catalog/countries")
321
+ return _decode_countries(result.data)
322
+
323
+ async def services(self, *, country_id: int | None = None) -> list[Service]:
324
+ result = await self._request(
325
+ "GET",
326
+ "/v1/catalog/services",
327
+ params=_services_params(country_id=country_id),
328
+ )
329
+ return _decode_services(result.data)
330
+
331
+ async def products(
332
+ self,
333
+ *,
334
+ country_id: int | None = None,
335
+ platform_id: int | None = None,
336
+ limit: int | None = None,
337
+ page: int | None = None,
338
+ sort: SortQuery | None = None,
339
+ ) -> ProductsPageV1:
340
+ result = await self._request(
341
+ "GET",
342
+ "/v1/catalog/products",
343
+ params=_products_params(
344
+ country_id=country_id,
345
+ platform_id=platform_id,
346
+ limit=limit,
347
+ page=page,
348
+ sort=sort,
349
+ ),
350
+ )
351
+ return _decode_v1_products(result)
352
+
353
+ async def exchange_rate(self, *, pair: str | None = None) -> ExchangeRate:
354
+ result = await self._request(
355
+ "GET",
356
+ "/v1/catalog/exchange-rate",
357
+ params=_exchange_rate_params(pair=pair),
358
+ )
359
+ return _decode_exchange_rate(result.data)