Skip to content

Commit 87ac37d

Browse files
committed
fixes + mypuy
1 parent 252f886 commit 87ac37d

File tree

12 files changed

+443
-301
lines changed

12 files changed

+443
-301
lines changed

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies = [
1717
"black>=25.1.0",
1818
"coverage>=7.8.0",
1919
"httpx[http2]>=0.28.1,<0.29.0",
20+
"mypy>=1.15.0",
2021
]
2122

2223
[dependency-groups]
@@ -54,5 +55,5 @@ line-length = 120
5455

5556
[tool.ruff]
5657
line-length = 120
57-
select = ["E", "F", "B", "I"] # Errors, Pyflakes, Bugbear, isort
58-
fix = true
58+
lint.select = ["E", "F", "B", "I"] # Errors, Pyflakes, Bugbear, isort
59+
fix = true

requirements.txt

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,46 @@ iniconfig==2.1.0 \
170170
--hash=sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7 \
171171
--hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760
172172
# via pytest
173+
mypy==1.15.0 \
174+
--hash=sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e \
175+
--hash=sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22 \
176+
--hash=sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f \
177+
--hash=sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2 \
178+
--hash=sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f \
179+
--hash=sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b \
180+
--hash=sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5 \
181+
--hash=sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f \
182+
--hash=sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43 \
183+
--hash=sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e \
184+
--hash=sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c \
185+
--hash=sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828 \
186+
--hash=sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba \
187+
--hash=sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee \
188+
--hash=sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d \
189+
--hash=sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b \
190+
--hash=sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445 \
191+
--hash=sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e \
192+
--hash=sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13 \
193+
--hash=sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5 \
194+
--hash=sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd \
195+
--hash=sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf \
196+
--hash=sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357 \
197+
--hash=sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b \
198+
--hash=sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036 \
199+
--hash=sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559 \
200+
--hash=sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3 \
201+
--hash=sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f \
202+
--hash=sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464 \
203+
--hash=sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980 \
204+
--hash=sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078 \
205+
--hash=sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5
206+
# via jpy-tillo-sdk
173207
mypy-extensions==1.1.0 \
174208
--hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \
175209
--hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558
176-
# via black
210+
# via
211+
# black
212+
# mypy
177213
nodeenv==1.9.1 \
178214
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
179215
--hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9
@@ -319,13 +355,15 @@ tomli==2.2.1 ; python_full_version < '3.11' \
319355
--hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7
320356
# via
321357
# black
358+
# mypy
322359
# pytest
323-
typing-extensions==4.13.2 ; python_full_version < '3.13' \
360+
typing-extensions==4.13.2 \
324361
--hash=sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c \
325362
--hash=sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef
326363
# via
327364
# anyio
328365
# black
366+
# mypy
329367
# pytest-asyncio
330368
virtualenv==20.31.2 \
331369
--hash=sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11 \

src/jpy_tillo_sdk/contracts.py

Lines changed: 98 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,75 @@ def brand(self):
2626
import logging
2727
import uuid
2828
from abc import ABC, abstractmethod
29+
from typing import Any, Optional
30+
31+
from httpx import Response
2932

30-
# Configure logging
3133
logger = logging.getLogger("tillo.contracts")
3234

3335

36+
class IssueDigitalCodeServiceInterface(ABC):
37+
@abstractmethod
38+
async def issue_digital_code(
39+
self,
40+
query_params: Optional[Any] = None,
41+
body: Optional[Any] = None,
42+
): ...
43+
44+
45+
class IssueDigitalCodeServiceAsyncInterface(ABC):
46+
@abstractmethod
47+
async def order_digital_code(
48+
self,
49+
query_params: Optional[Any] = None,
50+
body: Optional[Any] = None,
51+
): ...
52+
53+
54+
class TemplateServiceInterface(ABC):
55+
@abstractmethod
56+
def download_brand_template(
57+
self,
58+
query_params: Optional[Any] = None,
59+
) -> Any: ...
60+
61+
@abstractmethod
62+
def get_brand_templates(
63+
self,
64+
query_params: Optional[Any] = None,
65+
) -> Any: ...
66+
67+
68+
class TemplateServiceAsyncInterface(ABC):
69+
@abstractmethod
70+
async def download_brand_template(
71+
self,
72+
query_params: Optional[Any] = None,
73+
) -> Any: ...
74+
75+
@abstractmethod
76+
async def get_brand_templates(
77+
self,
78+
query_params: Optional[Any] = None,
79+
) -> Any: ...
80+
81+
82+
class FloatServiceAsyncInterface(ABC):
83+
@abstractmethod
84+
async def check_floats(
85+
self,
86+
query_params: Optional[Any] = None,
87+
) -> Response: ...
88+
89+
90+
class FloatServiceInterface(ABC):
91+
@abstractmethod
92+
def check_floats(
93+
self,
94+
query_params: Optional[Any] = None,
95+
) -> Response: ...
96+
97+
3498
class SignatureGeneratorInterface(ABC):
3599
"""Interface for generating secure signatures for Tillo API requests.
36100
@@ -55,8 +119,7 @@ def get_api_key(self) -> str:
55119
Returns:
56120
str: The API key used for Tillo API authentication
57121
"""
58-
logger.debug("Getting API key for authentication")
59-
pass
122+
...
60123

61124
@abstractmethod
62125
def get_secret_key_as_bytes(self) -> bytearray:
@@ -65,8 +128,7 @@ def get_secret_key_as_bytes(self) -> bytearray:
65128
Returns:
66129
bytearray: The secret key encoded as UTF-8 bytes
67130
"""
68-
logger.debug("Getting secret key as bytes for HMAC generation")
69-
pass
131+
...
70132

71133
@staticmethod
72134
@abstractmethod
@@ -76,8 +138,7 @@ def generate_timestamp() -> str:
76138
Returns:
77139
str: Current timestamp in milliseconds as a string
78140
"""
79-
logger.debug("Generating Unix timestamp in milliseconds")
80-
pass
141+
...
81142

82143
@staticmethod
83144
@abstractmethod
@@ -87,8 +148,7 @@ def generate_unique_client_request_id() -> uuid.UUID:
87148
Returns:
88149
uuid.UUID: A new UUID v4 for request identification
89150
"""
90-
logger.debug("Generating unique client request ID")
91-
pass
151+
...
92152

93153
@abstractmethod
94154
def generate_signature_string(self, endpoint: str, request_type: str, timestamp: str, params: tuple) -> str:
@@ -103,12 +163,7 @@ def generate_signature_string(self, endpoint: str, request_type: str, timestamp:
103163
Returns:
104164
str: The string to be signed according to Tillo's specification
105165
"""
106-
logger.debug(
107-
"Generating signature string for endpoint: %s, method: %s",
108-
endpoint,
109-
request_type,
110-
)
111-
pass
166+
...
112167

113168
@abstractmethod
114169
def generate_signature(self, seed: str) -> str:
@@ -120,8 +175,7 @@ def generate_signature(self, seed: str) -> str:
120175
Returns:
121176
str: The hexadecimal HMAC-SHA256 signature
122177
"""
123-
logger.debug("Generating HMAC-SHA256 signature")
124-
pass
178+
...
125179

126180

127181
class SignatureBridgeInterface(ABC):
@@ -157,12 +211,7 @@ def sign(
157211
Returns:
158212
tuple: A tuple containing (api_key, signature, timestamp)
159213
"""
160-
logger.debug(
161-
"Generating complete signature for endpoint: %s, method: %s",
162-
endpoint,
163-
method,
164-
)
165-
pass
214+
...
166215

167216

168217
class TilloInterface(ABC):
@@ -185,8 +234,9 @@ def brand(self):
185234
```
186235
"""
187236

237+
@property
188238
@abstractmethod
189-
def floats(self):
239+
def floats(self) -> FloatServiceInterface:
190240
"""Get the floats service instance.
191241
192242
Returns:
@@ -198,10 +248,11 @@ def floats(self):
198248
balance = float_service.get_balance()
199249
```
200250
"""
201-
pass
251+
...
202252

253+
@property
203254
@abstractmethod
204-
def floats_async(self):
255+
def floats_async(self) -> FloatServiceAsyncInterface:
205256
"""Get the asynchronous floats service instance.
206257
207258
Returns:
@@ -213,8 +264,9 @@ def floats_async(self):
213264
balance = float_service.get_balance()
214265
```
215266
"""
216-
pass
267+
...
217268

269+
@property
218270
@abstractmethod
219271
def brands(self):
220272
"""Get the brand service instance.
@@ -228,8 +280,9 @@ def brands(self):
228280
brand_info = brand_service.get_brand_details()
229281
```
230282
"""
231-
pass
283+
...
232284

285+
@property
233286
@abstractmethod
234287
def brands_async(self):
235288
"""Get the brand service instance.
@@ -243,10 +296,11 @@ def brands_async(self):
243296
brand_info = brand_service.get_brand_details()
244297
```
245298
"""
246-
pass
299+
...
247300

301+
@property
248302
@abstractmethod
249-
def templates(self):
303+
def templates(self) -> TemplateServiceInterface:
250304
"""Get the template service instance.
251305
252306
Returns:
@@ -258,10 +312,11 @@ def templates(self):
258312
templates = template_service.list_templates()
259313
```
260314
"""
261-
pass
315+
...
262316

317+
@property
263318
@abstractmethod
264-
async def templates_async(self):
319+
def templates_async(self) -> TemplateServiceAsyncInterface:
265320
"""Get the template service instance.
266321
267322
Returns:
@@ -273,10 +328,11 @@ async def templates_async(self):
273328
templates = template_service.list_templates()
274329
```
275330
"""
276-
pass
331+
...
277332

333+
@property
278334
@abstractmethod
279-
def digital_card(self):
335+
def digital_card(self) -> IssueDigitalCodeServiceInterface:
280336
"""Get the digital card service instance.
281337
282338
Returns:
@@ -288,11 +344,11 @@ def digital_card(self):
288344
card = digital_card_service.issue_card(amount=50.00)
289345
```
290346
"""
291-
logger.debug("Getting digital card service instance")
292-
pass
347+
...
293348

349+
@property
294350
@abstractmethod
295-
def digital_card_async(self):
351+
def digital_card_async(self) -> IssueDigitalCodeServiceAsyncInterface:
296352
"""Get the digital card service instance.
297353
298354
Returns:
@@ -304,9 +360,9 @@ def digital_card_async(self):
304360
card = digital_card_service.issue_card(amount=50.00)
305361
```
306362
"""
307-
logger.debug("Getting digital card service instance")
308-
pass
363+
...
309364

365+
@property
310366
@abstractmethod
311367
def physical_card(self):
312368
"""Get the physical card service instance.
@@ -320,9 +376,9 @@ def physical_card(self):
320376
card = physical_card_service.order_card(amount=100.00)
321377
```
322378
"""
323-
logger.debug("Getting physical card service instance")
324-
pass
379+
...
325380

381+
@property
326382
@abstractmethod
327383
def webhook(self):
328384
"""Get the webhook service instance.
@@ -336,5 +392,4 @@ def webhook(self):
336392
webhooks = webhook_service.list_webhooks()
337393
```
338394
"""
339-
logger.debug("Getting webhook service instance")
340-
pass
395+
...

src/jpy_tillo_sdk/domain/brand/services.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from typing import Any, Optional
33

4+
from ...contracts import TemplateServiceAsyncInterface, TemplateServiceInterface
45
from ...http_client import AsyncHttpClient, HttpClient
56
from .endpoints import BrandEndpoint, TemplateEndpoint, TemplateListEndpoint
67

@@ -44,7 +45,7 @@ async def get_available_brands(
4445
return await self.client.request(endpoint=endpoint)
4546

4647

47-
class TemplateService:
48+
class TemplateService(TemplateServiceInterface):
4849
def __init__(self, *, client: HttpClient):
4950
"""Initialize the float service with an HTTP client.
5051
@@ -69,7 +70,7 @@ def download_brand_template(
6970
return self.client.request(endpoint=endpoint)
7071

7172

72-
class TemplateServiceAsync:
73+
class TemplateServiceAsync(TemplateServiceAsyncInterface):
7374
def __init__(self, *, client: AsyncHttpClient):
7475
"""Initialize the float service with an HTTP client.
7576

0 commit comments

Comments
 (0)