maleo-database 0.0.4__py3-none-any.whl → 0.0.6__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.
- maleo/database/config/__init__.py +188 -15
- maleo/database/config/connection.py +46 -30
- maleo/database/config/pooling.py +171 -83
- maleo/database/dtos.py +6 -0
- maleo/database/managers/__init__.py +427 -0
- maleo/database/managers/client.py +82 -0
- maleo/database/managers/engine.py +64 -0
- maleo/database/managers/session.py +413 -137
- {maleo_database-0.0.4.dist-info → maleo_database-0.0.6.dist-info}/METADATA +23 -8
- maleo_database-0.0.6.dist-info/RECORD +26 -0
- maleo/database/managers/clients/__init__.py +0 -0
- maleo/database/managers/clients/elasticsearch.py +0 -66
- maleo/database/managers/clients/mongodb.py +0 -53
- maleo/database/managers/clients/redis.py +0 -57
- maleo/database/managers/engines/__init__.py +0 -0
- maleo/database/managers/engines/mysql.py +0 -56
- maleo/database/managers/engines/postgresql.py +0 -58
- maleo/database/managers/engines/sqlite.py +0 -55
- maleo/database/managers/engines/sqlserver.py +0 -63
- maleo_database-0.0.4.dist-info/RECORD +0 -32
- {maleo_database-0.0.4.dist-info → maleo_database-0.0.6.dist-info}/WHEEL +0 -0
- {maleo_database-0.0.4.dist-info → maleo_database-0.0.6.dist-info}/licenses/LICENSE +0 -0
- {maleo_database-0.0.4.dist-info → maleo_database-0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,427 @@
|
|
1
|
+
import traceback
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from datetime import datetime, timezone
|
4
|
+
from elasticsearch import AsyncElasticsearch, Elasticsearch
|
5
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
6
|
+
from pymongo import MongoClient
|
7
|
+
from redis.asyncio import Redis as AsyncRedis
|
8
|
+
from redis import Redis as SyncRedis
|
9
|
+
from sqlalchemy import MetaData, text
|
10
|
+
from typing import Generic, Optional, TypeVar
|
11
|
+
from uuid import uuid4
|
12
|
+
from maleo.dtos.authentication import GenericAuthentication
|
13
|
+
from maleo.dtos.contexts.operation import generate_operation_context
|
14
|
+
from maleo.dtos.contexts.request import RequestContext
|
15
|
+
from maleo.dtos.contexts.service import ServiceContext
|
16
|
+
from maleo.enums.operation import (
|
17
|
+
OperationType,
|
18
|
+
SystemOperationType,
|
19
|
+
Origin,
|
20
|
+
Layer,
|
21
|
+
Target,
|
22
|
+
)
|
23
|
+
from maleo.exceptions import MaleoException, InternalServerError
|
24
|
+
from maleo.logging.enums import Level
|
25
|
+
from maleo.logging.logger import Database
|
26
|
+
from maleo.mixins.timestamp import OperationTimestamp
|
27
|
+
from maleo.schemas.operation.system import (
|
28
|
+
SystemOperationAction,
|
29
|
+
SuccessfulSystemOperation,
|
30
|
+
)
|
31
|
+
from maleo.schemas.response import NoDataResponse
|
32
|
+
from maleo.types.base.uuid import OptionalUUID
|
33
|
+
from ..config import (
|
34
|
+
MySQLDatabaseConfig,
|
35
|
+
PostgreSQLDatabaseConfig,
|
36
|
+
SQLiteDatabaseConfig,
|
37
|
+
SQLServerDatabaseConfig,
|
38
|
+
SQLConfigT,
|
39
|
+
ElasticsearchDatabaseConfig,
|
40
|
+
MongoDBDatabaseConfig,
|
41
|
+
RedisDatabaseConfig,
|
42
|
+
NoSQLConfigT,
|
43
|
+
ConfigT,
|
44
|
+
)
|
45
|
+
from ..enums import Connection
|
46
|
+
from .client import (
|
47
|
+
AsyncClientT,
|
48
|
+
SyncClientT,
|
49
|
+
ClientManager,
|
50
|
+
)
|
51
|
+
from .engine import EngineManager
|
52
|
+
from .session import SessionManager
|
53
|
+
|
54
|
+
|
55
|
+
class DatabaseManager(ABC, Generic[ConfigT]):
|
56
|
+
def __init__(
|
57
|
+
self,
|
58
|
+
config: ConfigT,
|
59
|
+
logger: Database,
|
60
|
+
service_context: Optional[ServiceContext] = None,
|
61
|
+
) -> None:
|
62
|
+
super().__init__()
|
63
|
+
self._config = config
|
64
|
+
self._logger = logger
|
65
|
+
self._service_context = (
|
66
|
+
service_context
|
67
|
+
if service_context is not None
|
68
|
+
else ServiceContext.from_env()
|
69
|
+
)
|
70
|
+
self._operation_context = generate_operation_context(
|
71
|
+
origin=Origin.SERVICE,
|
72
|
+
layer=Layer.UTILITY,
|
73
|
+
target=Target.DATABASE,
|
74
|
+
)
|
75
|
+
|
76
|
+
@abstractmethod
|
77
|
+
async def async_check_connection(
|
78
|
+
self,
|
79
|
+
operation_id: OptionalUUID = None,
|
80
|
+
request_context: Optional[RequestContext] = None,
|
81
|
+
authentication: Optional[GenericAuthentication] = None,
|
82
|
+
) -> bool:
|
83
|
+
pass
|
84
|
+
|
85
|
+
@abstractmethod
|
86
|
+
def sync_check_connection(
|
87
|
+
self,
|
88
|
+
operation_id: OptionalUUID = None,
|
89
|
+
request_context: Optional[RequestContext] = None,
|
90
|
+
authentication: Optional[GenericAuthentication] = None,
|
91
|
+
) -> bool:
|
92
|
+
pass
|
93
|
+
|
94
|
+
@abstractmethod
|
95
|
+
async def dispose(self):
|
96
|
+
pass
|
97
|
+
|
98
|
+
|
99
|
+
class SQLDatabaseManager(DatabaseManager[SQLConfigT], Generic[SQLConfigT]):
|
100
|
+
def __init__(
|
101
|
+
self,
|
102
|
+
config: SQLConfigT,
|
103
|
+
metadata: MetaData,
|
104
|
+
logger: Database,
|
105
|
+
service_context: Optional[ServiceContext] = None,
|
106
|
+
) -> None:
|
107
|
+
super().__init__(config, logger, service_context)
|
108
|
+
self._metadata = metadata
|
109
|
+
self._operation_context.target.details = self._config.model_dump()
|
110
|
+
self._engine_manager = EngineManager[SQLConfigT](config)
|
111
|
+
self._session_manager = SessionManager(
|
112
|
+
config=config,
|
113
|
+
engines=self._engine_manager.get_all(),
|
114
|
+
logger=self._logger,
|
115
|
+
service_context=self._service_context,
|
116
|
+
)
|
117
|
+
self._metadata.create_all(bind=self._engine_manager.get(Connection.SYNC))
|
118
|
+
|
119
|
+
@property
|
120
|
+
def engine(self) -> EngineManager[SQLConfigT]:
|
121
|
+
return self._engine_manager
|
122
|
+
|
123
|
+
@property
|
124
|
+
def session(self) -> SessionManager:
|
125
|
+
return self._session_manager
|
126
|
+
|
127
|
+
async def async_check_connnection(
|
128
|
+
self,
|
129
|
+
operation_id: OptionalUUID = None,
|
130
|
+
request_context: Optional[RequestContext] = None,
|
131
|
+
authentication: Optional[GenericAuthentication] = None,
|
132
|
+
) -> bool:
|
133
|
+
"""Check database connectivity by executing a simple query."""
|
134
|
+
operation_id = operation_id if operation_id is not None else uuid4()
|
135
|
+
operation_action = SystemOperationAction(
|
136
|
+
type=SystemOperationType.DATABASE_CONNECTION, details=None
|
137
|
+
)
|
138
|
+
executed_at = datetime.now(tz=timezone.utc)
|
139
|
+
try:
|
140
|
+
async with self._session_manager.get(
|
141
|
+
Connection.ASYNC,
|
142
|
+
operation_id=operation_id,
|
143
|
+
request_context=request_context,
|
144
|
+
authentication=authentication,
|
145
|
+
) as session:
|
146
|
+
await session.execute(text("SELECT 1"))
|
147
|
+
completed_at = datetime.now(tz=timezone.utc)
|
148
|
+
operation_timestamp = OperationTimestamp(
|
149
|
+
executed_at=executed_at,
|
150
|
+
completed_at=completed_at,
|
151
|
+
duration=(completed_at - executed_at).total_seconds(),
|
152
|
+
)
|
153
|
+
SuccessfulSystemOperation[
|
154
|
+
Optional[GenericAuthentication], NoDataResponse[None]
|
155
|
+
](
|
156
|
+
service_context=self._service_context,
|
157
|
+
id=operation_id,
|
158
|
+
context=self._operation_context,
|
159
|
+
timestamp=operation_timestamp,
|
160
|
+
summary="Database connectivity check successful",
|
161
|
+
request_context=request_context,
|
162
|
+
authentication=authentication,
|
163
|
+
action=operation_action,
|
164
|
+
response=NoDataResponse[None](metadata=None, other=None),
|
165
|
+
).log(
|
166
|
+
self._logger, Level.INFO
|
167
|
+
)
|
168
|
+
return True
|
169
|
+
except MaleoException:
|
170
|
+
return False
|
171
|
+
except Exception as e:
|
172
|
+
completed_at = datetime.now(tz=timezone.utc)
|
173
|
+
operation_timestamp = OperationTimestamp(
|
174
|
+
executed_at=executed_at,
|
175
|
+
completed_at=completed_at,
|
176
|
+
duration=(completed_at - executed_at).total_seconds(),
|
177
|
+
)
|
178
|
+
error = InternalServerError[Optional[GenericAuthentication]](
|
179
|
+
OperationType.SYSTEM,
|
180
|
+
service_context=self._service_context,
|
181
|
+
operation_id=operation_id,
|
182
|
+
operation_context=self._operation_context,
|
183
|
+
operation_timestamp=operation_timestamp,
|
184
|
+
operation_summary="Unexpected error occured checking database connection",
|
185
|
+
request_context=request_context,
|
186
|
+
authentication=authentication,
|
187
|
+
operation_action=operation_action,
|
188
|
+
details={
|
189
|
+
"exc_type": type(e).__name__,
|
190
|
+
"exc_data": {
|
191
|
+
"message": str(e),
|
192
|
+
"args": e.args,
|
193
|
+
},
|
194
|
+
},
|
195
|
+
)
|
196
|
+
operation = error.generate_operation(OperationType.SYSTEM)
|
197
|
+
operation.log(self._logger, level=Level.ERROR)
|
198
|
+
return False
|
199
|
+
|
200
|
+
def sync_check_connection(
|
201
|
+
self,
|
202
|
+
operation_id: OptionalUUID = None,
|
203
|
+
request_context: Optional[RequestContext] = None,
|
204
|
+
authentication: Optional[GenericAuthentication] = None,
|
205
|
+
) -> bool:
|
206
|
+
"""Check database connectivity by executing a simple query."""
|
207
|
+
operation_id = operation_id if operation_id is not None else uuid4()
|
208
|
+
operation_action = SystemOperationAction(
|
209
|
+
type=SystemOperationType.DATABASE_CONNECTION, details=None
|
210
|
+
)
|
211
|
+
executed_at = datetime.now(tz=timezone.utc)
|
212
|
+
try:
|
213
|
+
with self._session_manager.get(
|
214
|
+
Connection.SYNC,
|
215
|
+
operation_id=operation_id,
|
216
|
+
request_context=request_context,
|
217
|
+
authentication=authentication,
|
218
|
+
) as session:
|
219
|
+
session.execute(text("SELECT 1"))
|
220
|
+
completed_at = datetime.now(tz=timezone.utc)
|
221
|
+
operation_timestamp = OperationTimestamp(
|
222
|
+
executed_at=executed_at,
|
223
|
+
completed_at=completed_at,
|
224
|
+
duration=(completed_at - executed_at).total_seconds(),
|
225
|
+
)
|
226
|
+
SuccessfulSystemOperation[
|
227
|
+
Optional[GenericAuthentication], NoDataResponse[None]
|
228
|
+
](
|
229
|
+
service_context=self._service_context,
|
230
|
+
id=operation_id,
|
231
|
+
context=self._operation_context,
|
232
|
+
timestamp=operation_timestamp,
|
233
|
+
summary="Database connectivity check successful",
|
234
|
+
request_context=request_context,
|
235
|
+
authentication=authentication,
|
236
|
+
action=operation_action,
|
237
|
+
response=NoDataResponse[None](metadata=None, other=None),
|
238
|
+
).log(
|
239
|
+
self._logger, Level.INFO
|
240
|
+
)
|
241
|
+
return True
|
242
|
+
except MaleoException:
|
243
|
+
return False
|
244
|
+
except Exception as e:
|
245
|
+
completed_at = datetime.now(tz=timezone.utc)
|
246
|
+
operation_timestamp = OperationTimestamp(
|
247
|
+
executed_at=executed_at,
|
248
|
+
completed_at=completed_at,
|
249
|
+
duration=(completed_at - executed_at).total_seconds(),
|
250
|
+
)
|
251
|
+
error = InternalServerError[Optional[GenericAuthentication]](
|
252
|
+
OperationType.SYSTEM,
|
253
|
+
service_context=self._service_context,
|
254
|
+
operation_id=operation_id,
|
255
|
+
operation_context=self._operation_context,
|
256
|
+
operation_timestamp=operation_timestamp,
|
257
|
+
operation_summary="Unexpected error occured checking database connection",
|
258
|
+
request_context=request_context,
|
259
|
+
authentication=authentication,
|
260
|
+
operation_action=operation_action,
|
261
|
+
details={
|
262
|
+
"exc_type": type(e).__name__,
|
263
|
+
"exc_data": {
|
264
|
+
"message": str(e),
|
265
|
+
"args": e.args,
|
266
|
+
},
|
267
|
+
},
|
268
|
+
)
|
269
|
+
operation = error.generate_operation(OperationType.SYSTEM)
|
270
|
+
operation.log(self._logger, level=Level.ERROR)
|
271
|
+
return False
|
272
|
+
|
273
|
+
async def dispose(self):
|
274
|
+
self._session_manager.dispose()
|
275
|
+
await self._engine_manager.dispose()
|
276
|
+
|
277
|
+
|
278
|
+
class MySQLDatabaseManager(SQLDatabaseManager[MySQLDatabaseConfig]):
|
279
|
+
pass
|
280
|
+
|
281
|
+
|
282
|
+
class PostgreSQLDatabaseManager(SQLDatabaseManager[PostgreSQLDatabaseConfig]):
|
283
|
+
pass
|
284
|
+
|
285
|
+
|
286
|
+
class SQLiteDatabaseManager(SQLDatabaseManager[SQLiteDatabaseConfig]):
|
287
|
+
pass
|
288
|
+
|
289
|
+
|
290
|
+
class SQLServerDatabaseManager(SQLDatabaseManager[SQLServerDatabaseConfig]):
|
291
|
+
pass
|
292
|
+
|
293
|
+
|
294
|
+
SQLDatabaseManagerT = TypeVar(
|
295
|
+
"SQLDatabaseManagerT",
|
296
|
+
MySQLDatabaseManager,
|
297
|
+
PostgreSQLDatabaseManager,
|
298
|
+
SQLiteDatabaseManager,
|
299
|
+
SQLServerDatabaseManager,
|
300
|
+
)
|
301
|
+
|
302
|
+
|
303
|
+
class NoSQLDatabaseManager(
|
304
|
+
DatabaseManager[NoSQLConfigT],
|
305
|
+
Generic[
|
306
|
+
NoSQLConfigT,
|
307
|
+
AsyncClientT,
|
308
|
+
SyncClientT,
|
309
|
+
],
|
310
|
+
):
|
311
|
+
def __init__(
|
312
|
+
self,
|
313
|
+
config: NoSQLConfigT,
|
314
|
+
metadata: MetaData,
|
315
|
+
logger: Database,
|
316
|
+
service_context: Optional[ServiceContext] = None,
|
317
|
+
) -> None:
|
318
|
+
super().__init__(config, logger, service_context)
|
319
|
+
self._metadata = metadata
|
320
|
+
self._operation_context.target.details = self._config.model_dump()
|
321
|
+
self._client_manager = ClientManager[
|
322
|
+
NoSQLConfigT,
|
323
|
+
AsyncClientT,
|
324
|
+
SyncClientT,
|
325
|
+
](config)
|
326
|
+
|
327
|
+
@property
|
328
|
+
def client(self) -> ClientManager[
|
329
|
+
NoSQLConfigT,
|
330
|
+
AsyncClientT,
|
331
|
+
SyncClientT,
|
332
|
+
]:
|
333
|
+
return self._client_manager
|
334
|
+
|
335
|
+
async def async_check_connnection(
|
336
|
+
self,
|
337
|
+
operation_id: OptionalUUID = None,
|
338
|
+
request_context: Optional[RequestContext] = None,
|
339
|
+
authentication: Optional[GenericAuthentication] = None,
|
340
|
+
) -> bool:
|
341
|
+
"""Check client connectivity by executing a simple query."""
|
342
|
+
client = self._client_manager.get(Connection.ASYNC)
|
343
|
+
try:
|
344
|
+
if isinstance(client, AsyncElasticsearch):
|
345
|
+
return await client.ping()
|
346
|
+
elif isinstance(client, AsyncIOMotorClient):
|
347
|
+
db = client.get_database(str(self._config.connection.database))
|
348
|
+
await db.command("ping")
|
349
|
+
return True
|
350
|
+
elif isinstance(client, AsyncRedis):
|
351
|
+
await client.ping()
|
352
|
+
return True
|
353
|
+
else:
|
354
|
+
raise TypeError(f"Invalid client type: '{type(client)}'")
|
355
|
+
except Exception:
|
356
|
+
print("Unable to check client connection:\n", traceback.format_exc())
|
357
|
+
return False
|
358
|
+
|
359
|
+
def sync_check_connection(
|
360
|
+
self,
|
361
|
+
operation_id: OptionalUUID = None,
|
362
|
+
request_context: Optional[RequestContext] = None,
|
363
|
+
authentication: Optional[GenericAuthentication] = None,
|
364
|
+
) -> bool:
|
365
|
+
"""Check client connectivity by executing a simple query."""
|
366
|
+
client = self._client_manager.get(Connection.SYNC)
|
367
|
+
try:
|
368
|
+
if isinstance(client, Elasticsearch):
|
369
|
+
return client.ping()
|
370
|
+
elif isinstance(client, MongoClient):
|
371
|
+
db = client.get_database(str(self._config.connection.database))
|
372
|
+
db.command("ping")
|
373
|
+
return True
|
374
|
+
elif isinstance(client, SyncRedis):
|
375
|
+
client.ping()
|
376
|
+
return True
|
377
|
+
else:
|
378
|
+
raise TypeError(f"Invalid client type: '{type(client)}'")
|
379
|
+
except Exception:
|
380
|
+
print("Unable to check client connection:\n", traceback.format_exc())
|
381
|
+
return False
|
382
|
+
|
383
|
+
async def dispose(self):
|
384
|
+
await self._client_manager.dispose()
|
385
|
+
|
386
|
+
|
387
|
+
class ElasticsearchDatabaseManager(
|
388
|
+
NoSQLDatabaseManager[ElasticsearchDatabaseConfig, AsyncElasticsearch, Elasticsearch]
|
389
|
+
):
|
390
|
+
pass
|
391
|
+
|
392
|
+
|
393
|
+
class MongoDBDatabaseManager(
|
394
|
+
NoSQLDatabaseManager[MongoDBDatabaseConfig, AsyncIOMotorClient, MongoClient]
|
395
|
+
):
|
396
|
+
pass
|
397
|
+
|
398
|
+
|
399
|
+
class RedisDatabaseManager(
|
400
|
+
NoSQLDatabaseManager[RedisDatabaseConfig, AsyncRedis, SyncRedis]
|
401
|
+
):
|
402
|
+
pass
|
403
|
+
|
404
|
+
|
405
|
+
NoSQLDatabaseManagerT = TypeVar(
|
406
|
+
"NoSQLDatabaseManagerT",
|
407
|
+
ElasticsearchDatabaseManager,
|
408
|
+
MongoDBDatabaseManager,
|
409
|
+
RedisDatabaseManager,
|
410
|
+
)
|
411
|
+
|
412
|
+
|
413
|
+
GenericDatabaseManagerT = TypeVar(
|
414
|
+
"GenericDatabaseManagerT", SQLDatabaseManager, NoSQLDatabaseManager
|
415
|
+
)
|
416
|
+
|
417
|
+
|
418
|
+
SpecificDatabaseManagerT = TypeVar(
|
419
|
+
"SpecificDatabaseManagerT",
|
420
|
+
MySQLDatabaseManager,
|
421
|
+
PostgreSQLDatabaseManager,
|
422
|
+
SQLiteDatabaseManager,
|
423
|
+
SQLServerDatabaseManager,
|
424
|
+
ElasticsearchDatabaseManager,
|
425
|
+
MongoDBDatabaseManager,
|
426
|
+
RedisDatabaseManager,
|
427
|
+
)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from elasticsearch import AsyncElasticsearch, Elasticsearch
|
2
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
3
|
+
from pymongo import MongoClient
|
4
|
+
from redis.asyncio import Redis as AsyncRedis
|
5
|
+
from redis import Redis as SyncRedis
|
6
|
+
from typing import Generic, Literal, Tuple, TypeVar, Union, overload
|
7
|
+
from ..config import (
|
8
|
+
ElasticsearchDatabaseConfig,
|
9
|
+
MongoDBDatabaseConfig,
|
10
|
+
RedisDatabaseConfig,
|
11
|
+
NoSQLConfigT,
|
12
|
+
)
|
13
|
+
from ..enums import Connection
|
14
|
+
|
15
|
+
|
16
|
+
AsyncClientT = TypeVar(
|
17
|
+
"AsyncClientT", AsyncElasticsearch, AsyncIOMotorClient, AsyncRedis
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
SyncClientT = TypeVar("SyncClientT", Elasticsearch, MongoClient, SyncRedis)
|
22
|
+
|
23
|
+
|
24
|
+
class ClientManager(
|
25
|
+
Generic[
|
26
|
+
NoSQLConfigT,
|
27
|
+
AsyncClientT,
|
28
|
+
SyncClientT,
|
29
|
+
]
|
30
|
+
):
|
31
|
+
def __init__(self, config: NoSQLConfigT) -> None:
|
32
|
+
super().__init__()
|
33
|
+
self._config = config
|
34
|
+
|
35
|
+
self._async_client = self._config.create_client(Connection.ASYNC)
|
36
|
+
self._sync_client = self._config.create_client(Connection.SYNC)
|
37
|
+
|
38
|
+
@overload
|
39
|
+
def get(self, connection: Literal[Connection.ASYNC]) -> AsyncClientT: ...
|
40
|
+
@overload
|
41
|
+
def get(self, connection: Literal[Connection.SYNC]) -> SyncClientT: ...
|
42
|
+
def get(
|
43
|
+
self, connection: Connection = Connection.ASYNC
|
44
|
+
) -> Union[AsyncClientT, SyncClientT]:
|
45
|
+
if connection is Connection.ASYNC:
|
46
|
+
return self._async_client
|
47
|
+
elif connection is Connection.SYNC:
|
48
|
+
return self._sync_client
|
49
|
+
|
50
|
+
def get_all(self) -> Tuple[AsyncClientT, SyncClientT]:
|
51
|
+
return (self._async_client, self._sync_client)
|
52
|
+
|
53
|
+
async def dispose(self):
|
54
|
+
if isinstance(self._async_client, AsyncIOMotorClient):
|
55
|
+
self._async_client.close()
|
56
|
+
else:
|
57
|
+
await self._async_client.close()
|
58
|
+
self._sync_client.close()
|
59
|
+
|
60
|
+
|
61
|
+
class ElasticsearchClientManager(
|
62
|
+
ClientManager[ElasticsearchDatabaseConfig, AsyncElasticsearch, Elasticsearch]
|
63
|
+
):
|
64
|
+
pass
|
65
|
+
|
66
|
+
|
67
|
+
class MongoDBClientManager(
|
68
|
+
ClientManager[MongoDBDatabaseConfig, AsyncIOMotorClient, MongoClient]
|
69
|
+
):
|
70
|
+
pass
|
71
|
+
|
72
|
+
|
73
|
+
class RedisClientManager(ClientManager[RedisDatabaseConfig, AsyncRedis, SyncRedis]):
|
74
|
+
pass
|
75
|
+
|
76
|
+
|
77
|
+
ClientManagerT = TypeVar(
|
78
|
+
"ClientManagerT",
|
79
|
+
ElasticsearchClientManager,
|
80
|
+
MongoDBClientManager,
|
81
|
+
RedisClientManager,
|
82
|
+
)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from sqlalchemy.engine import Engine
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
3
|
+
from typing import Generic, Literal, Tuple, TypeVar, Union, overload
|
4
|
+
from ..config import (
|
5
|
+
MySQLDatabaseConfig,
|
6
|
+
PostgreSQLDatabaseConfig,
|
7
|
+
SQLiteDatabaseConfig,
|
8
|
+
SQLServerDatabaseConfig,
|
9
|
+
SQLConfigT,
|
10
|
+
)
|
11
|
+
from ..enums import Connection
|
12
|
+
|
13
|
+
|
14
|
+
class EngineManager(Generic[SQLConfigT]):
|
15
|
+
def __init__(self, config: SQLConfigT) -> None:
|
16
|
+
super().__init__()
|
17
|
+
self._config = config
|
18
|
+
|
19
|
+
self._async_engine: AsyncEngine = self._config.create_engine(Connection.ASYNC)
|
20
|
+
self._sync_engine: Engine = self._config.create_engine(Connection.SYNC)
|
21
|
+
|
22
|
+
@overload
|
23
|
+
def get(self, connection: Literal[Connection.ASYNC]) -> AsyncEngine: ...
|
24
|
+
@overload
|
25
|
+
def get(self, connection: Literal[Connection.SYNC]) -> Engine: ...
|
26
|
+
def get(
|
27
|
+
self, connection: Connection = Connection.ASYNC
|
28
|
+
) -> Union[AsyncEngine, Engine]:
|
29
|
+
if connection is Connection.ASYNC:
|
30
|
+
return self._async_engine
|
31
|
+
elif connection is Connection.SYNC:
|
32
|
+
return self._sync_engine
|
33
|
+
|
34
|
+
def get_all(self) -> Tuple[AsyncEngine, Engine]:
|
35
|
+
return (self._async_engine, self._sync_engine)
|
36
|
+
|
37
|
+
async def dispose(self):
|
38
|
+
await self._async_engine.dispose()
|
39
|
+
self._sync_engine.dispose()
|
40
|
+
|
41
|
+
|
42
|
+
class MySQLEngineManager(EngineManager[MySQLDatabaseConfig]):
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
class PostgreSQLEngineManager(EngineManager[PostgreSQLDatabaseConfig]):
|
47
|
+
pass
|
48
|
+
|
49
|
+
|
50
|
+
class SQLiteEngineManager(EngineManager[SQLiteDatabaseConfig]):
|
51
|
+
pass
|
52
|
+
|
53
|
+
|
54
|
+
class SQLServerEngineManager(EngineManager[SQLServerDatabaseConfig]):
|
55
|
+
pass
|
56
|
+
|
57
|
+
|
58
|
+
EngineManagerT = TypeVar(
|
59
|
+
"EngineManagerT",
|
60
|
+
MySQLEngineManager,
|
61
|
+
PostgreSQLEngineManager,
|
62
|
+
SQLiteEngineManager,
|
63
|
+
SQLServerEngineManager,
|
64
|
+
)
|