qena-shared-lib 0.1.18__tar.gz → 0.1.20__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.18/README.md → qena_shared_lib-0.1.20/PKG-INFO +411 -21
- qena_shared_lib-0.1.18/PKG-INFO → qena_shared_lib-0.1.20/README.md +375 -51
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/pyproject.toml +26 -16
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/__init__.py +18 -1
- qena_shared_lib-0.1.20/src/qena_shared_lib/alias.py +27 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/cache.py +61 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/enums.py +8 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/eventbus.py +373 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/exceptions.py +2 -5
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/http/__init__.py +23 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/http/_request.py +24 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/http/_response.py +24 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/mongodb.py +599 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/__init__.py +2 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_listener.py +8 -32
- qena_shared_lib-0.1.20/src/qena_shared_lib/rabbitmq/message/__init__.py +22 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/rabbitmq/message/_inbound.py +13 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/rabbitmq/message/_outbound.py +13 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/redis.py +47 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/_base.py +14 -8
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/logstash/_base.py +1 -0
- qena_shared_lib-0.1.20/src/qena_shared_lib/sync.py +91 -0
- qena_shared_lib-0.1.18/.gitignore +0 -177
- qena_shared_lib-0.1.18/.pre-commit-config.yaml +0 -27
- qena_shared_lib-0.1.18/CHANGELOG.md +0 -81
- qena_shared_lib-0.1.18/tests/conftest.py +0 -32
- qena_shared_lib-0.1.18/tests/test_application.py +0 -629
- qena_shared_lib-0.1.18/tests/test_background.py +0 -87
- qena_shared_lib-0.1.18/tests/test_dependencies.py +0 -136
- qena_shared_lib-0.1.18/tests/test_kafka.py +0 -1211
- qena_shared_lib-0.1.18/tests/test_logstash.py +0 -585
- qena_shared_lib-0.1.18/tests/test_rabbitmq.py +0 -3509
- qena_shared_lib-0.1.18/tests/test_scheduler.py +0 -111
- qena_shared_lib-0.1.18/tests/test_security.py +0 -1244
- qena_shared_lib-0.1.18/tests/utils.py +0 -5
- qena_shared_lib-0.1.18/uv.lock +0 -1667
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/application.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/background.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/dependencies/__init__.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/dependencies/http.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/dependencies/miscellaneous.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/exception_handling.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/http/_base.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/http/_exception_handlers.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/kafka/__init__.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/kafka/_base.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/kafka/_consumer.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/kafka/_exception_handlers.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/kafka/_producer.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/logging.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/py.typed +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_base.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_channel.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_exception_handlers.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_pool.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_publisher.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/rabbitmq/_rpc_client.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/__init__.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/logstash/__init__.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/logstash/_http_sender.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/remotelogging/logstash/_tcp_sender.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/scheduler.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/security.py +0 -0
- {qena_shared_lib-0.1.18 → qena_shared_lib-0.1.20}/src/qena_shared_lib/utils.py +0 -0
|
@@ -1,26 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: qena-shared-lib
|
|
3
|
+
Version: 0.1.20
|
|
4
|
+
Summary: A shared tools for other services
|
|
5
|
+
Requires-Dist: fastapi[all]==0.115.6
|
|
6
|
+
Requires-Dist: prometheus-client==0.21.1
|
|
7
|
+
Requires-Dist: prometheus-fastapi-instrumentator==7.0.2
|
|
8
|
+
Requires-Dist: punq==0.7.0
|
|
9
|
+
Requires-Dist: pydantic-core==2.27.1
|
|
10
|
+
Requires-Dist: pydantic==2.10.3
|
|
11
|
+
Requires-Dist: starlette==0.41.3
|
|
12
|
+
Requires-Dist: typing-extensions==4.12.2
|
|
13
|
+
Requires-Dist: aiokafka==0.12.0 ; extra == 'all'
|
|
14
|
+
Requires-Dist: cronsim==2.6 ; extra == 'all'
|
|
15
|
+
Requires-Dist: jwt==1.3.1 ; extra == 'all'
|
|
16
|
+
Requires-Dist: passlib[bcrypt]==1.7.4 ; extra == 'all'
|
|
17
|
+
Requires-Dist: pika==1.3.2 ; extra == 'all'
|
|
18
|
+
Requires-Dist: pymongo==4.15.3 ; extra == 'all'
|
|
19
|
+
Requires-Dist: redis==7.1.0 ; extra == 'all'
|
|
20
|
+
Requires-Dist: aiokafka==0.12.0 ; extra == 'kafka'
|
|
21
|
+
Requires-Dist: pymongo==4.15.3 ; extra == 'mongodb'
|
|
22
|
+
Requires-Dist: pika==1.3.2 ; extra == 'rabbitmq'
|
|
23
|
+
Requires-Dist: redis==7.1.0 ; extra == 'redis'
|
|
24
|
+
Requires-Dist: cronsim==2.6 ; extra == 'scheduler'
|
|
25
|
+
Requires-Dist: jwt==1.3.1 ; extra == 'security'
|
|
26
|
+
Requires-Dist: passlib[bcrypt]==1.7.4 ; extra == 'security'
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Provides-Extra: kafka
|
|
30
|
+
Provides-Extra: mongodb
|
|
31
|
+
Provides-Extra: rabbitmq
|
|
32
|
+
Provides-Extra: redis
|
|
33
|
+
Provides-Extra: scheduler
|
|
34
|
+
Provides-Extra: security
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
1
37
|
# Qena shared lib
|
|
2
38
|
|
|
3
39
|
A shared tools for other services. It includes.
|
|
4
40
|
|
|
5
41
|
- FastAPI app builder
|
|
6
42
|
- A wrapper around fastapi to make it class based.
|
|
7
|
-
- RabbitMQ utility class to listen, respond, publish and make rpc request.
|
|
43
|
+
- RabbitMQ utility class to listen , respond , publish and make rpc request.
|
|
8
44
|
- Remote logging
|
|
9
45
|
- Logstash utility class to log message in `ecs` ( elastic common schema ).
|
|
10
|
-
- A simple task scheduler, to schedule task to run in specific time.
|
|
46
|
+
- A simple task scheduler , to schedule task to run in specific time.
|
|
11
47
|
- Background task runner.
|
|
12
|
-
- Security tools ( password hasher, jwt, acl ).
|
|
13
|
-
- IOC container to manager dependencies used across fastapi, rabbitmq manager and schedule manager.
|
|
48
|
+
- Security tools ( password hasher , jwt , acl ).
|
|
49
|
+
- IOC container to manager dependencies used across fastapi , rabbitmq manager and schedule manager.
|
|
50
|
+
- Kafka producer and consumer wrapper.
|
|
51
|
+
- Mongodb client wrapper , with repository and index manager.
|
|
52
|
+
- Redis wrapper with cache and distributed lock manager.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
14
55
|
|
|
15
|
-
|
|
56
|
+
It is prefered to use [astral.sh / uv](https://docs.astral.sh/uv) as a package manager.
|
|
16
57
|
|
|
17
|
-
|
|
58
|
+
``` sh
|
|
59
|
+
$ uv add qena-shared-lib[all]
|
|
60
|
+
```
|
|
61
|
+
to install all extras , or specific extras `kafka` , `rabbitmq` , `scheduler` , `security` , `redis` or `mongodb`.
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
- [Environment variables](#environment-variables)
|
|
66
|
+
- [Http](#http)
|
|
67
|
+
- [Lifespan](#lifespan)
|
|
68
|
+
- [Dependencies](#dependencies)
|
|
69
|
+
- [Controllers](#controllers)
|
|
70
|
+
- [Routers](#routers)
|
|
71
|
+
- [Remote logging](#remote-logging)
|
|
72
|
+
- [Logstash](#logstash)
|
|
73
|
+
- [Rabbitmq](#rabbitmq)
|
|
74
|
+
- [Publisher](#publisher)
|
|
75
|
+
- [RPC client](#rpc-client)
|
|
76
|
+
- [Flow control](#flow-control)
|
|
77
|
+
- [Rpc reply](#rpc-reply)
|
|
78
|
+
- [Retry consumer](#retry-consumer)
|
|
79
|
+
- [Scheduler](#scheduler)
|
|
80
|
+
- [Background](#background)
|
|
81
|
+
- [Security](#security)
|
|
82
|
+
- [Password hasher](#password-hasher)
|
|
83
|
+
- [JWT](#jwt)
|
|
84
|
+
- [ACL](#acl)
|
|
85
|
+
- [Kafka](#kafka)
|
|
86
|
+
- [Producer](#producer)
|
|
87
|
+
- [Consumer](#consumer)
|
|
88
|
+
- [Mongodb](#mongodb)
|
|
89
|
+
- [Aggregation](#aggregation)
|
|
90
|
+
- [Index](#index)
|
|
91
|
+
- [Crud](#crud)
|
|
92
|
+
- [Redis](#redis)
|
|
93
|
+
- [Cache](#cache)
|
|
94
|
+
- [Distribute lock](#distribute-lock)
|
|
95
|
+
|
|
96
|
+
### Environment variables
|
|
18
97
|
|
|
19
98
|
- `QENA_SHARED_LIB_LOGGING_LOGGER_NAME` root logger name.
|
|
20
99
|
- `QENA_SHARED_LIB_SECURITY_UNAUTHORIZED_RESPONSE_CODE` an integer response on an authorized access of resource.
|
|
21
100
|
- `QENA_SHARED_LIB_SECURITY_TOKEN_HEADER` to header key for jwt token.
|
|
22
101
|
|
|
23
|
-
|
|
102
|
+
### Http
|
|
24
103
|
|
|
25
104
|
To create fastapi app.
|
|
26
105
|
|
|
@@ -160,9 +239,10 @@ def main() -> FastAPI:
|
|
|
160
239
|
...
|
|
161
240
|
```
|
|
162
241
|
|
|
163
|
-
|
|
242
|
+
### Remote logging
|
|
243
|
+
|
|
244
|
+
#### Logstash
|
|
164
245
|
|
|
165
|
-
### Logstash
|
|
166
246
|
``` py
|
|
167
247
|
from qena_shared_lib.remotelogging import BaseRemoteLogSender
|
|
168
248
|
from qena_shared_lib.remotelogging.logstash import HTTPSender, # TCPSender
|
|
@@ -213,7 +293,7 @@ def log_message(
|
|
|
213
293
|
remote_logger.info(message)
|
|
214
294
|
```
|
|
215
295
|
|
|
216
|
-
|
|
296
|
+
### Rabbitmq
|
|
217
297
|
|
|
218
298
|
To create rabbitmq connection manager.
|
|
219
299
|
|
|
@@ -261,7 +341,7 @@ def main() -> FastAPI:
|
|
|
261
341
|
...
|
|
262
342
|
```
|
|
263
343
|
|
|
264
|
-
|
|
344
|
+
#### Publisher
|
|
265
345
|
|
|
266
346
|
``` py
|
|
267
347
|
@router.post("")
|
|
@@ -278,7 +358,7 @@ async def store_user(
|
|
|
278
358
|
# await publisher.publish_as_arguments(user)
|
|
279
359
|
```
|
|
280
360
|
|
|
281
|
-
|
|
361
|
+
#### RPC client
|
|
282
362
|
|
|
283
363
|
``` py
|
|
284
364
|
@router.get("")
|
|
@@ -297,7 +377,7 @@ async def get_user(
|
|
|
297
377
|
return user
|
|
298
378
|
```
|
|
299
379
|
|
|
300
|
-
|
|
380
|
+
#### Flow control
|
|
301
381
|
|
|
302
382
|
``` py
|
|
303
383
|
from qena_shared_lib.rabbitmq import ... , ListenerContext
|
|
@@ -316,7 +396,7 @@ class UserConsumer(ListenerBase):
|
|
|
316
396
|
|
|
317
397
|
```
|
|
318
398
|
|
|
319
|
-
|
|
399
|
+
#### Rpc reply
|
|
320
400
|
|
|
321
401
|
Optionally it is possible to reply to rpc calls, through.
|
|
322
402
|
|
|
@@ -336,7 +416,7 @@ class UserWorker(ListenerBase):
|
|
|
336
416
|
...
|
|
337
417
|
```
|
|
338
418
|
|
|
339
|
-
|
|
419
|
+
#### Retry consumer
|
|
340
420
|
|
|
341
421
|
Consumer can retry to consumer a message in an event of failure.
|
|
342
422
|
|
|
@@ -410,7 +490,7 @@ def main() -> FastAPI:
|
|
|
410
490
|
|
|
411
491
|
|
|
412
492
|
|
|
413
|
-
|
|
493
|
+
### Scheduler
|
|
414
494
|
|
|
415
495
|
``` py
|
|
416
496
|
from qena_shared_lib.scheduler import (
|
|
@@ -471,7 +551,7 @@ def main() -> FastAPI:
|
|
|
471
551
|
...
|
|
472
552
|
```
|
|
473
553
|
|
|
474
|
-
|
|
554
|
+
### Background
|
|
475
555
|
|
|
476
556
|
``` py
|
|
477
557
|
from qena_shared_lib.background import Background
|
|
@@ -515,9 +595,9 @@ async def process_data(
|
|
|
515
595
|
background.add_task(BackgroundTask(data_processor, data))
|
|
516
596
|
```
|
|
517
597
|
|
|
518
|
-
|
|
598
|
+
### Security
|
|
519
599
|
|
|
520
|
-
|
|
600
|
+
#### Password hasher
|
|
521
601
|
|
|
522
602
|
``` py
|
|
523
603
|
from qena_shared_lib.security import PasswordHasher
|
|
@@ -549,7 +629,7 @@ def main() -> FastAPI:
|
|
|
549
629
|
...
|
|
550
630
|
```
|
|
551
631
|
|
|
552
|
-
|
|
632
|
+
#### JWT
|
|
553
633
|
|
|
554
634
|
``` py
|
|
555
635
|
from qena_shared_lib.security import JwtAdapter
|
|
@@ -591,7 +671,7 @@ def main() -> FastAPI:
|
|
|
591
671
|
...
|
|
592
672
|
```
|
|
593
673
|
|
|
594
|
-
|
|
674
|
+
#### ACL
|
|
595
675
|
|
|
596
676
|
``` py
|
|
597
677
|
from qena_shared_lib.security import Authorization
|
|
@@ -625,3 +705,313 @@ async def get_users(
|
|
|
625
705
|
)
|
|
626
706
|
...
|
|
627
707
|
```
|
|
708
|
+
|
|
709
|
+
### Kafka
|
|
710
|
+
|
|
711
|
+
``` py
|
|
712
|
+
from qena_shared_lib.kafka import KafkaManager
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
@asynccontextmanager
|
|
716
|
+
async def lifespan(app: FastAPI):
|
|
717
|
+
kafka = get_service(KafkaManager)
|
|
718
|
+
|
|
719
|
+
await kafka.connect()
|
|
720
|
+
|
|
721
|
+
yield
|
|
722
|
+
|
|
723
|
+
await kafka.disconnect()
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def main() -> FastAPI:
|
|
727
|
+
...
|
|
728
|
+
|
|
729
|
+
kafka = KafkaManager(
|
|
730
|
+
remote_logger=...,
|
|
731
|
+
bootstrap_servers="127.0.0.1:9092",
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
builder.with_singleton(
|
|
735
|
+
service=KafkaManager,
|
|
736
|
+
instance=kafka,
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
...
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
#### Producer
|
|
743
|
+
|
|
744
|
+
``` py
|
|
745
|
+
class UserService:
|
|
746
|
+
def __init__(self, kafka: KafkaManager):
|
|
747
|
+
self._kafka = kafka
|
|
748
|
+
|
|
749
|
+
async def create_user(self):
|
|
750
|
+
...
|
|
751
|
+
|
|
752
|
+
async with await self._kafka.producer("user") as producer:
|
|
753
|
+
await producer.send(key="some_key", value=user)
|
|
754
|
+
|
|
755
|
+
...
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
#### Consumer
|
|
759
|
+
|
|
760
|
+
``` py
|
|
761
|
+
from qena_shared_lib.kafka import (
|
|
762
|
+
ConsumerBase,
|
|
763
|
+
consume,
|
|
764
|
+
consumer,
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
@consumer(["user"])
|
|
769
|
+
class USerConsumer(ConsumerBase):
|
|
770
|
+
def __init__(self, user_service: UserService):
|
|
771
|
+
self._user_service = user_service
|
|
772
|
+
|
|
773
|
+
@consume()
|
|
774
|
+
async def user_created(self, key: Any | None, value: Any | None):
|
|
775
|
+
...
|
|
776
|
+
|
|
777
|
+
await self._user_service.create_user(...)
|
|
778
|
+
|
|
779
|
+
...
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### Mongodb
|
|
783
|
+
|
|
784
|
+
``` py
|
|
785
|
+
from qena_shared_lib.mongodb import MongoDBManager
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
@asynccontextmanager
|
|
789
|
+
async def lifespan(app: FastAPI):
|
|
790
|
+
db = get_service(MongoDBManager)
|
|
791
|
+
|
|
792
|
+
await db.connect()
|
|
793
|
+
|
|
794
|
+
yield
|
|
795
|
+
|
|
796
|
+
await db.disconnect()
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def main() -> FastAPI:
|
|
800
|
+
...
|
|
801
|
+
|
|
802
|
+
db = MongoDBManager(
|
|
803
|
+
connection_string="mongodb://127.0.0.1:27017",
|
|
804
|
+
db="userDb"
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
builder.with_singleton(
|
|
808
|
+
service=MongoDBManager,
|
|
809
|
+
instance=db
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
...
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
#### Crud
|
|
816
|
+
|
|
817
|
+
``` py
|
|
818
|
+
from qena_shared_lib.mongodb import (
|
|
819
|
+
Document,
|
|
820
|
+
MongoDBManager,
|
|
821
|
+
ProjectedDocument,
|
|
822
|
+
RepositoryBase,
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
class User(Document):
|
|
827
|
+
__collection_name__ = "users"
|
|
828
|
+
|
|
829
|
+
full_name: str
|
|
830
|
+
phone: str
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
class FullNameProjectedUser(ProjectedDocument):
|
|
834
|
+
full_name: str
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
class UserRepository(RepositoryBase[User]):
|
|
838
|
+
pass
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
class UserService:
|
|
842
|
+
def __init__(self, user_repository: UserRepository):
|
|
843
|
+
self._user_repository = user_repository
|
|
844
|
+
|
|
845
|
+
async def add_user(self):
|
|
846
|
+
await self._user_repository.insert(
|
|
847
|
+
User(
|
|
848
|
+
full_name="user one",
|
|
849
|
+
phone="+251900000000"
|
|
850
|
+
)
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
async def get_user(self):
|
|
854
|
+
user = await self._user_repository.find_by_filter(
|
|
855
|
+
filter={"phone": "+251900000000"}
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
async def get_user_fullname(self):
|
|
859
|
+
user = await self._user_repository.find_by_filter(
|
|
860
|
+
filter={"phone": "+251900000000"}, projection=FullNameProjectedUser
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
async def update_user(self):
|
|
864
|
+
user = await self._user_repository.find_by_filter(
|
|
865
|
+
filter={"phone": "+251900000000"}, projection
|
|
866
|
+
)
|
|
867
|
+
user.phone = "+251900000001"
|
|
868
|
+
|
|
869
|
+
await user_repository.replace(user)
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### Aggregation
|
|
873
|
+
|
|
874
|
+
``` py
|
|
875
|
+
class AggregatedUser(AggregatedDocument):
|
|
876
|
+
__pipeline__ = [
|
|
877
|
+
{"$match": {"phone": {"$in": ["+251900000000", "+251900000001"]}}},
|
|
878
|
+
{"$project": {"fullName": True}},
|
|
879
|
+
]
|
|
880
|
+
|
|
881
|
+
full_name: str
|
|
882
|
+
|
|
883
|
+
class UserService:
|
|
884
|
+
...
|
|
885
|
+
|
|
886
|
+
async def get_user_fullnames(self):
|
|
887
|
+
users = user_repository.aggregate(aggregation=AggregatedUser)
|
|
888
|
+
|
|
889
|
+
...
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
#### Index
|
|
893
|
+
|
|
894
|
+
``` py
|
|
895
|
+
from qena_shared_lib.mongodb import Document, IndexManager, IndexModel
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
class User(Document):
|
|
899
|
+
__collection_name__ = "users"
|
|
900
|
+
__indexes__ = [IndexModel("phone")]
|
|
901
|
+
|
|
902
|
+
full_name: str
|
|
903
|
+
phone: str
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
async def manage_indexes():
|
|
907
|
+
...
|
|
908
|
+
|
|
909
|
+
index_manager = IndexManager(db=db, documents=[User])
|
|
910
|
+
|
|
911
|
+
await index_manager.create_indexes
|
|
912
|
+
|
|
913
|
+
...
|
|
914
|
+
|
|
915
|
+
await index_manager.drop_indexes()
|
|
916
|
+
|
|
917
|
+
...
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
### Redis
|
|
921
|
+
|
|
922
|
+
``` py
|
|
923
|
+
from qena_shared_lib.redis import RedisManager
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
@asynccontextmanager
|
|
927
|
+
async def lifespan(app: FastAPI):
|
|
928
|
+
redis_manager = get_service(RedisManager)
|
|
929
|
+
|
|
930
|
+
await dredis_managerb.connect()
|
|
931
|
+
|
|
932
|
+
yield
|
|
933
|
+
|
|
934
|
+
await redis_manager.disconnect()
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def main() -> FastAPI:
|
|
938
|
+
...
|
|
939
|
+
|
|
940
|
+
redis_manager = RedisManager("redis://127.0.0.1:6379")
|
|
941
|
+
|
|
942
|
+
builder.with_singleton(
|
|
943
|
+
service=RedisManager,
|
|
944
|
+
instance=redis_manager
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
...
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
#### Cache
|
|
951
|
+
|
|
952
|
+
``` py
|
|
953
|
+
from qena_shared_lib.cache import CachedObject, CacheManager
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def main() -> FastAPI:
|
|
957
|
+
...
|
|
958
|
+
|
|
959
|
+
cache_manager = CacheManager()
|
|
960
|
+
|
|
961
|
+
redis_manager.add(cache_manager)
|
|
962
|
+
builder.with_singleton(
|
|
963
|
+
service=CacheManager,
|
|
964
|
+
instance=cache_manager
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
...
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
class UserCache(CachedObject):
|
|
971
|
+
full_name: str
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class UserService:
|
|
975
|
+
def __init__(self, cache_manager: CacheManager):
|
|
976
|
+
self._cache_manager = cache_manager
|
|
977
|
+
|
|
978
|
+
async def cache_user(self):
|
|
979
|
+
await self._cache_manager.set(
|
|
980
|
+
UserCache(full_name="user one")
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
async def get_cached_user(self):
|
|
984
|
+
user_cache = await self._cache_manager.get(UserCache)
|
|
985
|
+
|
|
986
|
+
async def unset_cached_user(self):
|
|
987
|
+
await self._cache_manager.unset(UserCache)
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
#### Distribute lock
|
|
991
|
+
|
|
992
|
+
``` py
|
|
993
|
+
from qena_shared_lib.sync import DistributedLockManager
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def main() -> FastAPI:
|
|
997
|
+
...
|
|
998
|
+
|
|
999
|
+
distributed_lock_manager = DistributedLockManager()
|
|
1000
|
+
|
|
1001
|
+
redis_manager.add(distributed_lock_manager)
|
|
1002
|
+
builder.with_singleton(
|
|
1003
|
+
service=DistributedLockManager,
|
|
1004
|
+
instance=distributed_lock_manager
|
|
1005
|
+
)
|
|
1006
|
+
|
|
1007
|
+
...
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
class UserService:
|
|
1011
|
+
def __init__(self, distributed_lock_manager: DistributedLockManager):
|
|
1012
|
+
self._distributed_lock_manager = distributed_lock_manager
|
|
1013
|
+
|
|
1014
|
+
async def create_user(self):
|
|
1015
|
+
async with self._distributed_lock_manager("user_one_create") as _:
|
|
1016
|
+
...
|
|
1017
|
+
```
|