qena-shared-lib 0.1.8__tar.gz → 0.1.9__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.
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/PKG-INFO +146 -12
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/README.md +145 -11
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/pyproject.toml +1 -1
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/exception_handlers.py +2 -2
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/http.py +16 -40
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_exception_handlers.py +2 -2
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/.gitignore +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/.pre-commit-config.yaml +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/requirements.txt +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/__init__.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/application.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/background.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/dependencies/__init__.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/dependencies/http.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/dependencies/miscellaneous.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/exceptions.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logging.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logstash/__init__.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logstash/_base.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logstash/_http_sender.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logstash/_tcp_sender.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/py.typed +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/__init__.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_base.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_channel.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_listener.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_pool.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_publisher.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_rpc_client.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/scheduler.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/security.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/utils.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/conftest.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_application.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_background.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_dependencies.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_logstash.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_rabbitmq.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_scheduler.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/test_security.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/tests/utils.py +0 -0
- {qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/uv.lock +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qena-shared-lib
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.9
|
4
4
|
Summary: A shared tools for other services
|
5
5
|
Requires-Python: >=3.10
|
6
6
|
Requires-Dist: cronsim==2.6
|
@@ -40,6 +40,9 @@ A shared tools for other services. It includes.
|
|
40
40
|
To create fastapi app.
|
41
41
|
|
42
42
|
``` py
|
43
|
+
from qena_shared_lib.application import Builder, Environment
|
44
|
+
|
45
|
+
|
43
46
|
def main() -> FastAPI:
|
44
47
|
builder = (
|
45
48
|
Builder()
|
@@ -63,6 +66,11 @@ $ uvicorn --factory main:main
|
|
63
66
|
### Lifespan
|
64
67
|
|
65
68
|
``` py
|
69
|
+
from contextlib import asynccontextmanager
|
70
|
+
|
71
|
+
from fastapi import FastAPI
|
72
|
+
|
73
|
+
|
66
74
|
@asynccontextmanager
|
67
75
|
def lifespan(app: FastAPI):
|
68
76
|
...
|
@@ -83,6 +91,16 @@ def main() -> FastAPI:
|
|
83
91
|
### Dependencies
|
84
92
|
|
85
93
|
``` py
|
94
|
+
class EmailService:
|
95
|
+
def __init__(self):
|
96
|
+
...
|
97
|
+
|
98
|
+
|
99
|
+
class Database:
|
100
|
+
def __init__(self):
|
101
|
+
...
|
102
|
+
|
103
|
+
|
86
104
|
def main() -> FastAPI:
|
87
105
|
...
|
88
106
|
|
@@ -95,7 +113,10 @@ def main() -> FastAPI:
|
|
95
113
|
### Controllers
|
96
114
|
|
97
115
|
``` py
|
98
|
-
|
116
|
+
from qena_shared_lib.http import ControllerBase, api_controller, post
|
117
|
+
|
118
|
+
|
119
|
+
@api_controller("/users")
|
99
120
|
class UserController(ControllerBase):
|
100
121
|
|
101
122
|
def __init__(self, email_service: EmailService):
|
@@ -112,11 +133,18 @@ def main() -> FastAPI:
|
|
112
133
|
builder.with_controllers([
|
113
134
|
UserController
|
114
135
|
])
|
136
|
+
|
137
|
+
...
|
115
138
|
```
|
116
139
|
|
117
140
|
### Routers
|
118
141
|
|
119
142
|
``` py
|
143
|
+
from fastapi import APIRouter
|
144
|
+
|
145
|
+
from qena_shared_lib.dependencies.http import DependsOn
|
146
|
+
|
147
|
+
|
120
148
|
router = APIRouter(prefix="/auth")
|
121
149
|
|
122
150
|
|
@@ -153,6 +181,9 @@ def main() -> FastAPI:
|
|
153
181
|
## Logstash
|
154
182
|
|
155
183
|
``` py
|
184
|
+
from qena_shared_lib.logstash import BaseLogstashSender, HTTPSender, # TCPSender
|
185
|
+
|
186
|
+
|
156
187
|
@asynccontextmanager
|
157
188
|
async def lifespan(app: FastAPI):
|
158
189
|
logstash = get_service(BaseLogstashSender)
|
@@ -203,6 +234,9 @@ def log_message(
|
|
203
234
|
To create rabbitmq connection manager.
|
204
235
|
|
205
236
|
``` py
|
237
|
+
from qena_shared_lib.rabbitmq import ListenerBase, consume, consumer
|
238
|
+
|
239
|
+
|
206
240
|
@asynccontextmanager
|
207
241
|
async def lifespan(app: FastAPI):
|
208
242
|
rabbitmq = get_service(RabbitMqManager)
|
@@ -214,7 +248,7 @@ async def lifespan(app: FastAPI):
|
|
214
248
|
rabbitmq.disconnect()
|
215
249
|
|
216
250
|
|
217
|
-
@
|
251
|
+
@consumer("UserQueue")
|
218
252
|
class UserConsumer(ListenerBase):
|
219
253
|
|
220
254
|
def __init__(self, db: Database):
|
@@ -249,7 +283,7 @@ def main() -> FastAPI:
|
|
249
283
|
async def store_user(
|
250
284
|
rabbitmq: Annotated[
|
251
285
|
RabbitMqManager,
|
252
|
-
|
286
|
+
DependsOn(RabbitMqManager)
|
253
287
|
],
|
254
288
|
user: User,
|
255
289
|
)
|
@@ -266,7 +300,7 @@ async def store_user(
|
|
266
300
|
async def get_user(
|
267
301
|
rabbitmq: Annotated[
|
268
302
|
RabbitMqManager,
|
269
|
-
|
303
|
+
DependsOn(RabbitMqManager)
|
270
304
|
],
|
271
305
|
user_id: str,
|
272
306
|
)
|
@@ -281,7 +315,10 @@ async def get_user(
|
|
281
315
|
### Flow control
|
282
316
|
|
283
317
|
``` py
|
284
|
-
|
318
|
+
from qena_shared_lib.rabbitmq import ... , ListenerContext
|
319
|
+
|
320
|
+
|
321
|
+
@consumer("UserQueue")
|
285
322
|
class UserConsumer(ListenerBase):
|
286
323
|
|
287
324
|
@consume()
|
@@ -299,7 +336,10 @@ class UserConsumer(ListenerBase):
|
|
299
336
|
Optionally it is possible to reply to rpc calls, through.
|
300
337
|
|
301
338
|
``` py
|
302
|
-
|
339
|
+
from qena_shared_lib.rabbitmq import ... , rpc_worker
|
340
|
+
|
341
|
+
|
342
|
+
@rpc_worker("UserQueue")
|
303
343
|
class UserWorker(ListenerBase):
|
304
344
|
|
305
345
|
@execute()
|
@@ -311,10 +351,92 @@ class UserWorker(ListenerBase):
|
|
311
351
|
...
|
312
352
|
```
|
313
353
|
|
354
|
+
### Retry consumer
|
355
|
+
|
356
|
+
Consumer can retry to consumer a message in an event of failure.
|
357
|
+
|
358
|
+
``` py
|
359
|
+
from qena_shared_lib.rabbitmq import (
|
360
|
+
BackoffRetryDelay,
|
361
|
+
FixedRetryDelay,
|
362
|
+
RabbitMqManager,
|
363
|
+
RetryDelayJitter,
|
364
|
+
RetryPolicy,
|
365
|
+
)
|
366
|
+
|
367
|
+
|
368
|
+
@consumer(
|
369
|
+
queue="UserQueue",
|
370
|
+
# can be defined for consumer of specific queue
|
371
|
+
retry_policy=RetryPolicy(
|
372
|
+
exceptions=(AMQPError,),
|
373
|
+
max_retry=5,
|
374
|
+
retry_delay_strategy=FixedRetryDelay(
|
375
|
+
retry_delay=2
|
376
|
+
),
|
377
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
378
|
+
)
|
379
|
+
)
|
380
|
+
class UserConsumer(ListenerBase):
|
381
|
+
|
382
|
+
@consume(
|
383
|
+
# for specific target
|
384
|
+
retry_policy=RetryPolicy(
|
385
|
+
exceptions=(AMQPError,),
|
386
|
+
max_retry=5,
|
387
|
+
retry_delay_strategy=FixedRetryDelay(
|
388
|
+
retry_delay=2
|
389
|
+
),
|
390
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
391
|
+
)
|
392
|
+
)
|
393
|
+
async def store_user(self, ctx: ListenerContext, user: User):
|
394
|
+
...
|
395
|
+
|
396
|
+
await ctx.flow_control.request(10)
|
397
|
+
|
398
|
+
...
|
399
|
+
|
400
|
+
|
401
|
+
def main() -> FastAPI:
|
402
|
+
...
|
403
|
+
|
404
|
+
rabbitmq = RabbitMqManager(
|
405
|
+
logstash=logstash,
|
406
|
+
container=builder.container,
|
407
|
+
# or globally for all consumers
|
408
|
+
listener_global_retry_policy=RetryPolicy(
|
409
|
+
exceptions=(AMQPError,),
|
410
|
+
max_retry=10,
|
411
|
+
retry_delay_strategy=BackoffRetryDelay(
|
412
|
+
multiplier=1.5, min=2, max=10
|
413
|
+
),
|
414
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
415
|
+
match_by_cause=True,
|
416
|
+
),
|
417
|
+
)
|
418
|
+
|
419
|
+
rabbitmq.include_listener(UserConsumer)
|
420
|
+
builder.add_singleton(
|
421
|
+
service=RabbitMqManager,
|
422
|
+
instance=rabbitmq,
|
423
|
+
)
|
424
|
+
```
|
425
|
+
|
426
|
+
|
314
427
|
|
315
428
|
## Scheduler
|
316
429
|
|
317
430
|
``` py
|
431
|
+
from qena_shared_lib.scheduler import (
|
432
|
+
ScheduleManager,
|
433
|
+
# Scheduler,
|
434
|
+
SchedulerBase,
|
435
|
+
schedule,
|
436
|
+
scheduler,
|
437
|
+
)
|
438
|
+
|
439
|
+
|
318
440
|
@asynccontextmanager
|
319
441
|
async def lifespan(app: FastAPI):
|
320
442
|
schedule_manager = get_service(ScheduleManager)
|
@@ -326,7 +448,7 @@ async def lifespan(app: FastAPI):
|
|
326
448
|
schedule_manager.stop()
|
327
449
|
|
328
450
|
|
329
|
-
@
|
451
|
+
@scheduler()
|
330
452
|
class TaskScheduler(SchedulerBase):
|
331
453
|
|
332
454
|
def __init__(self, db: Database)
|
@@ -367,6 +489,9 @@ def main() -> FastAPI:
|
|
367
489
|
## Background
|
368
490
|
|
369
491
|
``` py
|
492
|
+
from qena_shared_lib.background import Background
|
493
|
+
|
494
|
+
|
370
495
|
@asynccontextmanager
|
371
496
|
async def lifespan(app: FastAPI):
|
372
497
|
background = get_service(Background)
|
@@ -410,7 +535,10 @@ async def process_data(
|
|
410
535
|
### Password hasher
|
411
536
|
|
412
537
|
``` py
|
413
|
-
|
538
|
+
from qena_shared_lib.security import PasswordHasher
|
539
|
+
|
540
|
+
|
541
|
+
@api_controller("/users")
|
414
542
|
class UserController(ControllerBase):
|
415
543
|
|
416
544
|
def __init__(self, password_hasher: PasswordHasher):
|
@@ -439,6 +567,9 @@ def main() -> FastAPI:
|
|
439
567
|
### JWT
|
440
568
|
|
441
569
|
``` py
|
570
|
+
from qena_shared_lib.security import JwtAdapter
|
571
|
+
|
572
|
+
|
442
573
|
@ApiController("/users")
|
443
574
|
class UserController(ControllerBase):
|
444
575
|
|
@@ -478,7 +609,10 @@ def main() -> FastAPI:
|
|
478
609
|
### ACL
|
479
610
|
|
480
611
|
``` py
|
481
|
-
|
612
|
+
from qena_shared_lib.security import Authorization
|
613
|
+
|
614
|
+
|
615
|
+
@api_controller("/users")
|
482
616
|
class UserController(ControllerBase):
|
483
617
|
|
484
618
|
@post()
|
@@ -486,7 +620,7 @@ class UserController(ControllerBase):
|
|
486
620
|
self,
|
487
621
|
user: Annotated[
|
488
622
|
UserInfo,
|
489
|
-
|
623
|
+
Authorization(
|
490
624
|
user_type="ADMIN",
|
491
625
|
persmission=[
|
492
626
|
"READ"
|
@@ -501,7 +635,7 @@ class UserController(ControllerBase):
|
|
501
635
|
async def get_users(
|
502
636
|
user: Annotated[
|
503
637
|
UserInfo,
|
504
|
-
|
638
|
+
Authorization("ADMIN")
|
505
639
|
]
|
506
640
|
)
|
507
641
|
...
|
@@ -24,6 +24,9 @@ A shared tools for other services. It includes.
|
|
24
24
|
To create fastapi app.
|
25
25
|
|
26
26
|
``` py
|
27
|
+
from qena_shared_lib.application import Builder, Environment
|
28
|
+
|
29
|
+
|
27
30
|
def main() -> FastAPI:
|
28
31
|
builder = (
|
29
32
|
Builder()
|
@@ -47,6 +50,11 @@ $ uvicorn --factory main:main
|
|
47
50
|
### Lifespan
|
48
51
|
|
49
52
|
``` py
|
53
|
+
from contextlib import asynccontextmanager
|
54
|
+
|
55
|
+
from fastapi import FastAPI
|
56
|
+
|
57
|
+
|
50
58
|
@asynccontextmanager
|
51
59
|
def lifespan(app: FastAPI):
|
52
60
|
...
|
@@ -67,6 +75,16 @@ def main() -> FastAPI:
|
|
67
75
|
### Dependencies
|
68
76
|
|
69
77
|
``` py
|
78
|
+
class EmailService:
|
79
|
+
def __init__(self):
|
80
|
+
...
|
81
|
+
|
82
|
+
|
83
|
+
class Database:
|
84
|
+
def __init__(self):
|
85
|
+
...
|
86
|
+
|
87
|
+
|
70
88
|
def main() -> FastAPI:
|
71
89
|
...
|
72
90
|
|
@@ -79,7 +97,10 @@ def main() -> FastAPI:
|
|
79
97
|
### Controllers
|
80
98
|
|
81
99
|
``` py
|
82
|
-
|
100
|
+
from qena_shared_lib.http import ControllerBase, api_controller, post
|
101
|
+
|
102
|
+
|
103
|
+
@api_controller("/users")
|
83
104
|
class UserController(ControllerBase):
|
84
105
|
|
85
106
|
def __init__(self, email_service: EmailService):
|
@@ -96,11 +117,18 @@ def main() -> FastAPI:
|
|
96
117
|
builder.with_controllers([
|
97
118
|
UserController
|
98
119
|
])
|
120
|
+
|
121
|
+
...
|
99
122
|
```
|
100
123
|
|
101
124
|
### Routers
|
102
125
|
|
103
126
|
``` py
|
127
|
+
from fastapi import APIRouter
|
128
|
+
|
129
|
+
from qena_shared_lib.dependencies.http import DependsOn
|
130
|
+
|
131
|
+
|
104
132
|
router = APIRouter(prefix="/auth")
|
105
133
|
|
106
134
|
|
@@ -137,6 +165,9 @@ def main() -> FastAPI:
|
|
137
165
|
## Logstash
|
138
166
|
|
139
167
|
``` py
|
168
|
+
from qena_shared_lib.logstash import BaseLogstashSender, HTTPSender, # TCPSender
|
169
|
+
|
170
|
+
|
140
171
|
@asynccontextmanager
|
141
172
|
async def lifespan(app: FastAPI):
|
142
173
|
logstash = get_service(BaseLogstashSender)
|
@@ -187,6 +218,9 @@ def log_message(
|
|
187
218
|
To create rabbitmq connection manager.
|
188
219
|
|
189
220
|
``` py
|
221
|
+
from qena_shared_lib.rabbitmq import ListenerBase, consume, consumer
|
222
|
+
|
223
|
+
|
190
224
|
@asynccontextmanager
|
191
225
|
async def lifespan(app: FastAPI):
|
192
226
|
rabbitmq = get_service(RabbitMqManager)
|
@@ -198,7 +232,7 @@ async def lifespan(app: FastAPI):
|
|
198
232
|
rabbitmq.disconnect()
|
199
233
|
|
200
234
|
|
201
|
-
@
|
235
|
+
@consumer("UserQueue")
|
202
236
|
class UserConsumer(ListenerBase):
|
203
237
|
|
204
238
|
def __init__(self, db: Database):
|
@@ -233,7 +267,7 @@ def main() -> FastAPI:
|
|
233
267
|
async def store_user(
|
234
268
|
rabbitmq: Annotated[
|
235
269
|
RabbitMqManager,
|
236
|
-
|
270
|
+
DependsOn(RabbitMqManager)
|
237
271
|
],
|
238
272
|
user: User,
|
239
273
|
)
|
@@ -250,7 +284,7 @@ async def store_user(
|
|
250
284
|
async def get_user(
|
251
285
|
rabbitmq: Annotated[
|
252
286
|
RabbitMqManager,
|
253
|
-
|
287
|
+
DependsOn(RabbitMqManager)
|
254
288
|
],
|
255
289
|
user_id: str,
|
256
290
|
)
|
@@ -265,7 +299,10 @@ async def get_user(
|
|
265
299
|
### Flow control
|
266
300
|
|
267
301
|
``` py
|
268
|
-
|
302
|
+
from qena_shared_lib.rabbitmq import ... , ListenerContext
|
303
|
+
|
304
|
+
|
305
|
+
@consumer("UserQueue")
|
269
306
|
class UserConsumer(ListenerBase):
|
270
307
|
|
271
308
|
@consume()
|
@@ -283,7 +320,10 @@ class UserConsumer(ListenerBase):
|
|
283
320
|
Optionally it is possible to reply to rpc calls, through.
|
284
321
|
|
285
322
|
``` py
|
286
|
-
|
323
|
+
from qena_shared_lib.rabbitmq import ... , rpc_worker
|
324
|
+
|
325
|
+
|
326
|
+
@rpc_worker("UserQueue")
|
287
327
|
class UserWorker(ListenerBase):
|
288
328
|
|
289
329
|
@execute()
|
@@ -295,10 +335,92 @@ class UserWorker(ListenerBase):
|
|
295
335
|
...
|
296
336
|
```
|
297
337
|
|
338
|
+
### Retry consumer
|
339
|
+
|
340
|
+
Consumer can retry to consumer a message in an event of failure.
|
341
|
+
|
342
|
+
``` py
|
343
|
+
from qena_shared_lib.rabbitmq import (
|
344
|
+
BackoffRetryDelay,
|
345
|
+
FixedRetryDelay,
|
346
|
+
RabbitMqManager,
|
347
|
+
RetryDelayJitter,
|
348
|
+
RetryPolicy,
|
349
|
+
)
|
350
|
+
|
351
|
+
|
352
|
+
@consumer(
|
353
|
+
queue="UserQueue",
|
354
|
+
# can be defined for consumer of specific queue
|
355
|
+
retry_policy=RetryPolicy(
|
356
|
+
exceptions=(AMQPError,),
|
357
|
+
max_retry=5,
|
358
|
+
retry_delay_strategy=FixedRetryDelay(
|
359
|
+
retry_delay=2
|
360
|
+
),
|
361
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
362
|
+
)
|
363
|
+
)
|
364
|
+
class UserConsumer(ListenerBase):
|
365
|
+
|
366
|
+
@consume(
|
367
|
+
# for specific target
|
368
|
+
retry_policy=RetryPolicy(
|
369
|
+
exceptions=(AMQPError,),
|
370
|
+
max_retry=5,
|
371
|
+
retry_delay_strategy=FixedRetryDelay(
|
372
|
+
retry_delay=2
|
373
|
+
),
|
374
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
375
|
+
)
|
376
|
+
)
|
377
|
+
async def store_user(self, ctx: ListenerContext, user: User):
|
378
|
+
...
|
379
|
+
|
380
|
+
await ctx.flow_control.request(10)
|
381
|
+
|
382
|
+
...
|
383
|
+
|
384
|
+
|
385
|
+
def main() -> FastAPI:
|
386
|
+
...
|
387
|
+
|
388
|
+
rabbitmq = RabbitMqManager(
|
389
|
+
logstash=logstash,
|
390
|
+
container=builder.container,
|
391
|
+
# or globally for all consumers
|
392
|
+
listener_global_retry_policy=RetryPolicy(
|
393
|
+
exceptions=(AMQPError,),
|
394
|
+
max_retry=10,
|
395
|
+
retry_delay_strategy=BackoffRetryDelay(
|
396
|
+
multiplier=1.5, min=2, max=10
|
397
|
+
),
|
398
|
+
retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
|
399
|
+
match_by_cause=True,
|
400
|
+
),
|
401
|
+
)
|
402
|
+
|
403
|
+
rabbitmq.include_listener(UserConsumer)
|
404
|
+
builder.add_singleton(
|
405
|
+
service=RabbitMqManager,
|
406
|
+
instance=rabbitmq,
|
407
|
+
)
|
408
|
+
```
|
409
|
+
|
410
|
+
|
298
411
|
|
299
412
|
## Scheduler
|
300
413
|
|
301
414
|
``` py
|
415
|
+
from qena_shared_lib.scheduler import (
|
416
|
+
ScheduleManager,
|
417
|
+
# Scheduler,
|
418
|
+
SchedulerBase,
|
419
|
+
schedule,
|
420
|
+
scheduler,
|
421
|
+
)
|
422
|
+
|
423
|
+
|
302
424
|
@asynccontextmanager
|
303
425
|
async def lifespan(app: FastAPI):
|
304
426
|
schedule_manager = get_service(ScheduleManager)
|
@@ -310,7 +432,7 @@ async def lifespan(app: FastAPI):
|
|
310
432
|
schedule_manager.stop()
|
311
433
|
|
312
434
|
|
313
|
-
@
|
435
|
+
@scheduler()
|
314
436
|
class TaskScheduler(SchedulerBase):
|
315
437
|
|
316
438
|
def __init__(self, db: Database)
|
@@ -351,6 +473,9 @@ def main() -> FastAPI:
|
|
351
473
|
## Background
|
352
474
|
|
353
475
|
``` py
|
476
|
+
from qena_shared_lib.background import Background
|
477
|
+
|
478
|
+
|
354
479
|
@asynccontextmanager
|
355
480
|
async def lifespan(app: FastAPI):
|
356
481
|
background = get_service(Background)
|
@@ -394,7 +519,10 @@ async def process_data(
|
|
394
519
|
### Password hasher
|
395
520
|
|
396
521
|
``` py
|
397
|
-
|
522
|
+
from qena_shared_lib.security import PasswordHasher
|
523
|
+
|
524
|
+
|
525
|
+
@api_controller("/users")
|
398
526
|
class UserController(ControllerBase):
|
399
527
|
|
400
528
|
def __init__(self, password_hasher: PasswordHasher):
|
@@ -423,6 +551,9 @@ def main() -> FastAPI:
|
|
423
551
|
### JWT
|
424
552
|
|
425
553
|
``` py
|
554
|
+
from qena_shared_lib.security import JwtAdapter
|
555
|
+
|
556
|
+
|
426
557
|
@ApiController("/users")
|
427
558
|
class UserController(ControllerBase):
|
428
559
|
|
@@ -462,7 +593,10 @@ def main() -> FastAPI:
|
|
462
593
|
### ACL
|
463
594
|
|
464
595
|
``` py
|
465
|
-
|
596
|
+
from qena_shared_lib.security import Authorization
|
597
|
+
|
598
|
+
|
599
|
+
@api_controller("/users")
|
466
600
|
class UserController(ControllerBase):
|
467
601
|
|
468
602
|
@post()
|
@@ -470,7 +604,7 @@ class UserController(ControllerBase):
|
|
470
604
|
self,
|
471
605
|
user: Annotated[
|
472
606
|
UserInfo,
|
473
|
-
|
607
|
+
Authorization(
|
474
608
|
user_type="ADMIN",
|
475
609
|
persmission=[
|
476
610
|
"READ"
|
@@ -485,7 +619,7 @@ class UserController(ControllerBase):
|
|
485
619
|
async def get_users(
|
486
620
|
user: Annotated[
|
487
621
|
UserInfo,
|
488
|
-
|
622
|
+
Authorization("ADMIN")
|
489
623
|
]
|
490
624
|
)
|
491
625
|
...
|
@@ -121,8 +121,8 @@ def handle_http_service_error(
|
|
121
121
|
if exception.logstash_logging:
|
122
122
|
logstash_logger_method(
|
123
123
|
message=exception.message,
|
124
|
-
tags=
|
125
|
-
extra=
|
124
|
+
tags=tags,
|
125
|
+
extra=extra,
|
126
126
|
exception=exception if exception.extract_exc_info else None,
|
127
127
|
)
|
128
128
|
else:
|
@@ -10,7 +10,6 @@ from fastapi.types import IncEx
|
|
10
10
|
|
11
11
|
__all__ = [
|
12
12
|
"api_controller",
|
13
|
-
"ApiController",
|
14
13
|
"ControllerBase",
|
15
14
|
"delete",
|
16
15
|
"get",
|
@@ -63,20 +62,19 @@ class RouteHandlerMeta:
|
|
63
62
|
openapi_extra: dict[str, Any] | None = None
|
64
63
|
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
self._api_router = APIRouter(
|
65
|
+
def api_controller(
|
66
|
+
prefix: str | None = None,
|
67
|
+
*,
|
68
|
+
tags: list[str | Enum] | None = None,
|
69
|
+
dependencies: Sequence[Depends] | None = None,
|
70
|
+
default_response_class: type[Response] = JSONResponse,
|
71
|
+
responses: dict[int | str, dict[str, Any]] | None = None,
|
72
|
+
redirect_slashes: bool = True,
|
73
|
+
deprecated: bool | None = None,
|
74
|
+
include_in_schema: bool = True,
|
75
|
+
) -> Callable[["ControllerBase"], "ControllerBase"]:
|
76
|
+
def annotate_class(api_controller_class: ControllerBase):
|
77
|
+
router = APIRouter(
|
80
78
|
prefix=prefix or "",
|
81
79
|
tags=tags,
|
82
80
|
dependencies=dependencies,
|
@@ -87,33 +85,11 @@ class ApiController:
|
|
87
85
|
include_in_schema=include_in_schema,
|
88
86
|
)
|
89
87
|
|
90
|
-
|
91
|
-
setattr(controller, API_CONTROLLER_ATTRIBUTE, self._api_router)
|
92
|
-
|
93
|
-
return controller
|
88
|
+
setattr(api_controller_class, API_CONTROLLER_ATTRIBUTE, router)
|
94
89
|
|
90
|
+
return api_controller_class
|
95
91
|
|
96
|
-
|
97
|
-
prefix: str | None = None,
|
98
|
-
*,
|
99
|
-
tags: list[str | Enum] | None = None,
|
100
|
-
dependencies: Sequence[Depends] | None = None,
|
101
|
-
default_response_class: type[Response] = JSONResponse,
|
102
|
-
responses: dict[int | str, dict[str, Any]] | None = None,
|
103
|
-
redirect_slashes: bool = True,
|
104
|
-
deprecated: bool | None = None,
|
105
|
-
include_in_schema: bool = True,
|
106
|
-
) -> ApiController:
|
107
|
-
return ApiController(
|
108
|
-
prefix=prefix,
|
109
|
-
tags=tags,
|
110
|
-
dependencies=dependencies,
|
111
|
-
default_response_class=default_response_class,
|
112
|
-
responses=responses,
|
113
|
-
redirect_slashes=redirect_slashes,
|
114
|
-
deprecated=deprecated,
|
115
|
-
include_in_schema=include_in_schema,
|
116
|
-
)
|
92
|
+
return annotate_class
|
117
93
|
|
118
94
|
|
119
95
|
def get(
|
{qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/rabbitmq/_exception_handlers.py
RENAMED
@@ -87,8 +87,8 @@ def handle_rabbit_mq_service_exception(
|
|
87
87
|
if exception.logstash_logging:
|
88
88
|
logstash_logger_method(
|
89
89
|
message=exception.message,
|
90
|
-
tags=
|
91
|
-
extra=
|
90
|
+
tags=tags,
|
91
|
+
extra=extra,
|
92
92
|
exception=exception if exception.extract_exc_info else None,
|
93
93
|
)
|
94
94
|
else:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/dependencies/__init__.py
RENAMED
File without changes
|
File without changes
|
{qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/dependencies/miscellaneous.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{qena_shared_lib-0.1.8 → qena_shared_lib-0.1.9}/src/qena_shared_lib/logstash/_http_sender.py
RENAMED
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
|