mdb-engine 0.1.6__py3-none-any.whl → 0.4.12__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.
- mdb_engine/__init__.py +116 -11
- mdb_engine/auth/ARCHITECTURE.md +112 -0
- mdb_engine/auth/README.md +654 -11
- mdb_engine/auth/__init__.py +136 -29
- mdb_engine/auth/audit.py +592 -0
- mdb_engine/auth/base.py +252 -0
- mdb_engine/auth/casbin_factory.py +265 -70
- mdb_engine/auth/config_defaults.py +5 -5
- mdb_engine/auth/config_helpers.py +19 -18
- mdb_engine/auth/cookie_utils.py +12 -16
- mdb_engine/auth/csrf.py +483 -0
- mdb_engine/auth/decorators.py +10 -16
- mdb_engine/auth/dependencies.py +69 -71
- mdb_engine/auth/helpers.py +3 -3
- mdb_engine/auth/integration.py +61 -88
- mdb_engine/auth/jwt.py +11 -15
- mdb_engine/auth/middleware.py +79 -35
- mdb_engine/auth/oso_factory.py +21 -41
- mdb_engine/auth/provider.py +270 -171
- mdb_engine/auth/rate_limiter.py +505 -0
- mdb_engine/auth/restrictions.py +21 -36
- mdb_engine/auth/session_manager.py +24 -41
- mdb_engine/auth/shared_middleware.py +977 -0
- mdb_engine/auth/shared_users.py +775 -0
- mdb_engine/auth/token_lifecycle.py +10 -12
- mdb_engine/auth/token_store.py +17 -32
- mdb_engine/auth/users.py +99 -159
- mdb_engine/auth/utils.py +236 -42
- mdb_engine/cli/commands/generate.py +546 -10
- mdb_engine/cli/commands/validate.py +3 -7
- mdb_engine/cli/utils.py +7 -7
- mdb_engine/config.py +13 -28
- mdb_engine/constants.py +65 -0
- mdb_engine/core/README.md +117 -6
- mdb_engine/core/__init__.py +39 -7
- mdb_engine/core/app_registration.py +31 -50
- mdb_engine/core/app_secrets.py +289 -0
- mdb_engine/core/connection.py +20 -12
- mdb_engine/core/encryption.py +222 -0
- mdb_engine/core/engine.py +2862 -115
- mdb_engine/core/index_management.py +12 -16
- mdb_engine/core/manifest.py +628 -204
- mdb_engine/core/ray_integration.py +436 -0
- mdb_engine/core/seeding.py +13 -21
- mdb_engine/core/service_initialization.py +20 -30
- mdb_engine/core/types.py +40 -43
- mdb_engine/database/README.md +140 -17
- mdb_engine/database/__init__.py +17 -6
- mdb_engine/database/abstraction.py +37 -50
- mdb_engine/database/connection.py +51 -30
- mdb_engine/database/query_validator.py +367 -0
- mdb_engine/database/resource_limiter.py +204 -0
- mdb_engine/database/scoped_wrapper.py +747 -237
- mdb_engine/dependencies.py +427 -0
- mdb_engine/di/__init__.py +34 -0
- mdb_engine/di/container.py +247 -0
- mdb_engine/di/providers.py +206 -0
- mdb_engine/di/scopes.py +139 -0
- mdb_engine/embeddings/README.md +54 -24
- mdb_engine/embeddings/__init__.py +31 -24
- mdb_engine/embeddings/dependencies.py +38 -155
- mdb_engine/embeddings/service.py +78 -75
- mdb_engine/exceptions.py +104 -12
- mdb_engine/indexes/README.md +30 -13
- mdb_engine/indexes/__init__.py +1 -0
- mdb_engine/indexes/helpers.py +11 -11
- mdb_engine/indexes/manager.py +59 -123
- mdb_engine/memory/README.md +95 -4
- mdb_engine/memory/__init__.py +1 -2
- mdb_engine/memory/service.py +363 -1168
- mdb_engine/observability/README.md +4 -2
- mdb_engine/observability/__init__.py +26 -9
- mdb_engine/observability/health.py +17 -17
- mdb_engine/observability/logging.py +10 -10
- mdb_engine/observability/metrics.py +40 -19
- mdb_engine/repositories/__init__.py +34 -0
- mdb_engine/repositories/base.py +325 -0
- mdb_engine/repositories/mongo.py +233 -0
- mdb_engine/repositories/unit_of_work.py +166 -0
- mdb_engine/routing/README.md +1 -1
- mdb_engine/routing/__init__.py +1 -3
- mdb_engine/routing/websockets.py +41 -75
- mdb_engine/utils/__init__.py +3 -1
- mdb_engine/utils/mongo.py +117 -0
- mdb_engine-0.4.12.dist-info/METADATA +492 -0
- mdb_engine-0.4.12.dist-info/RECORD +97 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/WHEEL +1 -1
- mdb_engine-0.1.6.dist-info/METADATA +0 -213
- mdb_engine-0.1.6.dist-info/RECORD +0 -75
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/entry_points.txt +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/licenses/LICENSE +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/top_level.txt +0 -0
|
@@ -99,9 +99,11 @@ record_operation(
|
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
# Record failed operation
|
|
102
|
+
from pymongo.errors import PyMongoError
|
|
103
|
+
|
|
102
104
|
try:
|
|
103
105
|
await db.collection.insert_one(document)
|
|
104
|
-
except
|
|
106
|
+
except PyMongoError as e:
|
|
105
107
|
record_operation(
|
|
106
108
|
operation_name="database.insert_one",
|
|
107
109
|
duration_ms=0,
|
|
@@ -368,7 +370,7 @@ async def check_custom_service():
|
|
|
368
370
|
message="Service responded with error",
|
|
369
371
|
details={"status_code": response.status_code}
|
|
370
372
|
)
|
|
371
|
-
except
|
|
373
|
+
except (httpx.HTTPError, ConnectionError, TimeoutError) as e:
|
|
372
374
|
return HealthCheckResult(
|
|
373
375
|
name="custom_service",
|
|
374
376
|
status=HealthStatus.UNHEALTHY,
|
|
@@ -5,15 +5,32 @@ Provides structured logging, metrics collection, request tracing,
|
|
|
5
5
|
and health check capabilities.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from .health import (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
from .health import (
|
|
9
|
+
HealthChecker,
|
|
10
|
+
HealthCheckResult,
|
|
11
|
+
HealthStatus,
|
|
12
|
+
check_engine_health,
|
|
13
|
+
check_mongodb_health,
|
|
14
|
+
check_pool_health,
|
|
15
|
+
)
|
|
16
|
+
from .logging import (
|
|
17
|
+
ContextualLoggerAdapter,
|
|
18
|
+
clear_app_context,
|
|
19
|
+
clear_correlation_id,
|
|
20
|
+
get_correlation_id,
|
|
21
|
+
get_logger,
|
|
22
|
+
get_logging_context,
|
|
23
|
+
log_operation,
|
|
24
|
+
set_app_context,
|
|
25
|
+
set_correlation_id,
|
|
26
|
+
)
|
|
27
|
+
from .metrics import (
|
|
28
|
+
MetricsCollector,
|
|
29
|
+
OperationMetrics,
|
|
30
|
+
get_metrics_collector,
|
|
31
|
+
record_operation,
|
|
32
|
+
timed_operation,
|
|
33
|
+
)
|
|
17
34
|
|
|
18
35
|
__all__ = [
|
|
19
36
|
# Metrics
|
|
@@ -6,13 +6,17 @@ Provides health check functions for monitoring system status.
|
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import logging
|
|
9
|
+
from collections.abc import Callable
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
from enum import Enum
|
|
12
|
-
from typing import Any
|
|
13
|
+
from typing import Any
|
|
13
14
|
|
|
14
|
-
from pymongo.errors import (
|
|
15
|
-
|
|
15
|
+
from pymongo.errors import (
|
|
16
|
+
ConnectionFailure,
|
|
17
|
+
OperationFailure,
|
|
18
|
+
ServerSelectionTimeoutError,
|
|
19
|
+
)
|
|
16
20
|
|
|
17
21
|
logger = logging.getLogger(__name__)
|
|
18
22
|
|
|
@@ -33,10 +37,10 @@ class HealthCheckResult:
|
|
|
33
37
|
name: str
|
|
34
38
|
status: HealthStatus
|
|
35
39
|
message: str
|
|
36
|
-
details:
|
|
40
|
+
details: dict[str, Any] | None = None
|
|
37
41
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
38
42
|
|
|
39
|
-
def to_dict(self) ->
|
|
43
|
+
def to_dict(self) -> dict[str, Any]:
|
|
40
44
|
"""Convert to dictionary."""
|
|
41
45
|
return {
|
|
42
46
|
"name": self.name,
|
|
@@ -54,7 +58,7 @@ class HealthChecker:
|
|
|
54
58
|
|
|
55
59
|
def __init__(self):
|
|
56
60
|
"""Initialize the health checker."""
|
|
57
|
-
self._checks:
|
|
61
|
+
self._checks: list[callable] = []
|
|
58
62
|
|
|
59
63
|
def register_check(self, check_func: Callable) -> None:
|
|
60
64
|
"""
|
|
@@ -65,14 +69,14 @@ class HealthChecker:
|
|
|
65
69
|
"""
|
|
66
70
|
self._checks.append(check_func)
|
|
67
71
|
|
|
68
|
-
async def check_all(self) ->
|
|
72
|
+
async def check_all(self) -> dict[str, Any]:
|
|
69
73
|
"""
|
|
70
74
|
Run all registered health checks.
|
|
71
75
|
|
|
72
76
|
Returns:
|
|
73
77
|
Dictionary with overall status and individual check results
|
|
74
78
|
"""
|
|
75
|
-
results:
|
|
79
|
+
results: list[HealthCheckResult] = []
|
|
76
80
|
|
|
77
81
|
for check_func in self._checks:
|
|
78
82
|
try:
|
|
@@ -86,9 +90,7 @@ class HealthChecker:
|
|
|
86
90
|
ConnectionError,
|
|
87
91
|
OSError,
|
|
88
92
|
) as e:
|
|
89
|
-
logger.error(
|
|
90
|
-
f"Health check {check_func.__name__} failed: {e}", exc_info=True
|
|
91
|
-
)
|
|
93
|
+
logger.error(f"Health check {check_func.__name__} failed: {e}", exc_info=True)
|
|
92
94
|
results.append(
|
|
93
95
|
HealthCheckResult(
|
|
94
96
|
name=check_func.__name__,
|
|
@@ -116,7 +118,7 @@ class HealthChecker:
|
|
|
116
118
|
|
|
117
119
|
|
|
118
120
|
async def check_mongodb_health(
|
|
119
|
-
mongo_client:
|
|
121
|
+
mongo_client: Any | None, timeout_seconds: float = 5.0
|
|
120
122
|
) -> HealthCheckResult:
|
|
121
123
|
"""
|
|
122
124
|
Check MongoDB connection health.
|
|
@@ -137,9 +139,7 @@ async def check_mongodb_health(
|
|
|
137
139
|
|
|
138
140
|
try:
|
|
139
141
|
# Try to ping MongoDB with timeout
|
|
140
|
-
await asyncio.wait_for(
|
|
141
|
-
mongo_client.admin.command("ping"), timeout=timeout_seconds
|
|
142
|
-
)
|
|
142
|
+
await asyncio.wait_for(mongo_client.admin.command("ping"), timeout=timeout_seconds)
|
|
143
143
|
|
|
144
144
|
return HealthCheckResult(
|
|
145
145
|
name="mongodb",
|
|
@@ -167,7 +167,7 @@ async def check_mongodb_health(
|
|
|
167
167
|
)
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
async def check_engine_health(engine:
|
|
170
|
+
async def check_engine_health(engine: Any | None) -> HealthCheckResult:
|
|
171
171
|
"""
|
|
172
172
|
Check MongoDB Engine health.
|
|
173
173
|
|
|
@@ -206,7 +206,7 @@ async def check_engine_health(engine: Optional[Any]) -> HealthCheckResult:
|
|
|
206
206
|
|
|
207
207
|
|
|
208
208
|
async def check_pool_health(
|
|
209
|
-
get_pool_metrics_func:
|
|
209
|
+
get_pool_metrics_func: Callable[[], Any] | None = None,
|
|
210
210
|
) -> HealthCheckResult:
|
|
211
211
|
"""
|
|
212
212
|
Check connection pool health.
|
|
@@ -8,25 +8,25 @@ import contextvars
|
|
|
8
8
|
import logging
|
|
9
9
|
import uuid
|
|
10
10
|
from datetime import datetime
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
12
12
|
|
|
13
13
|
# Context variable for correlation ID
|
|
14
|
-
_correlation_id: contextvars.ContextVar[
|
|
14
|
+
_correlation_id: contextvars.ContextVar[str | None] = contextvars.ContextVar(
|
|
15
15
|
"correlation_id", default=None
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
# Context variable for app context
|
|
19
|
-
_app_context: contextvars.ContextVar[
|
|
19
|
+
_app_context: contextvars.ContextVar[dict[str, Any] | None] = contextvars.ContextVar(
|
|
20
20
|
"app_context", default=None
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def get_correlation_id() ->
|
|
24
|
+
def get_correlation_id() -> str | None:
|
|
25
25
|
"""Get the current correlation ID from context."""
|
|
26
26
|
return _correlation_id.get()
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def set_correlation_id(correlation_id:
|
|
29
|
+
def set_correlation_id(correlation_id: str | None = None) -> str:
|
|
30
30
|
"""
|
|
31
31
|
Set a correlation ID in the current context.
|
|
32
32
|
|
|
@@ -47,7 +47,7 @@ def clear_correlation_id() -> None:
|
|
|
47
47
|
_correlation_id.set(None)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def set_app_context(app_slug:
|
|
50
|
+
def set_app_context(app_slug: str | None = None, **kwargs: Any) -> None:
|
|
51
51
|
"""
|
|
52
52
|
Set app context for logging.
|
|
53
53
|
|
|
@@ -64,14 +64,14 @@ def clear_app_context() -> None:
|
|
|
64
64
|
_app_context.set(None)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def get_logging_context() ->
|
|
67
|
+
def get_logging_context() -> dict[str, Any]:
|
|
68
68
|
"""
|
|
69
69
|
Get current logging context (correlation ID and app context).
|
|
70
70
|
|
|
71
71
|
Returns:
|
|
72
72
|
Dictionary with context information
|
|
73
73
|
"""
|
|
74
|
-
context:
|
|
74
|
+
context: dict[str, Any] = {
|
|
75
75
|
"timestamp": datetime.now().isoformat(),
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -91,7 +91,7 @@ class ContextualLoggerAdapter(logging.LoggerAdapter):
|
|
|
91
91
|
Logger adapter that automatically adds context to log records.
|
|
92
92
|
"""
|
|
93
93
|
|
|
94
|
-
def process(self, msg: str, kwargs:
|
|
94
|
+
def process(self, msg: str, kwargs: dict[str, Any]) -> tuple[str, dict[str, Any]]:
|
|
95
95
|
"""Add context to log records."""
|
|
96
96
|
# Get base context
|
|
97
97
|
context = get_logging_context()
|
|
@@ -124,7 +124,7 @@ def log_operation(
|
|
|
124
124
|
operation: str,
|
|
125
125
|
level: int = logging.INFO,
|
|
126
126
|
success: bool = True,
|
|
127
|
-
duration_ms:
|
|
127
|
+
duration_ms: float | None = None,
|
|
128
128
|
**context: Any,
|
|
129
129
|
) -> None:
|
|
130
130
|
"""
|
|
@@ -9,9 +9,10 @@ import logging
|
|
|
9
9
|
import threading
|
|
10
10
|
import time
|
|
11
11
|
from collections import OrderedDict
|
|
12
|
+
from collections.abc import Callable
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from datetime import datetime
|
|
14
|
-
from typing import Any
|
|
15
|
+
from typing import Any
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
@@ -26,7 +27,7 @@ class OperationMetrics:
|
|
|
26
27
|
min_duration_ms: float = float("inf")
|
|
27
28
|
max_duration_ms: float = 0.0
|
|
28
29
|
error_count: int = 0
|
|
29
|
-
last_execution:
|
|
30
|
+
last_execution: datetime | None = None
|
|
30
31
|
|
|
31
32
|
@property
|
|
32
33
|
def avg_duration_ms(self) -> float:
|
|
@@ -48,23 +49,19 @@ class OperationMetrics:
|
|
|
48
49
|
self.error_count += 1
|
|
49
50
|
self.last_execution = datetime.now()
|
|
50
51
|
|
|
51
|
-
def to_dict(self) ->
|
|
52
|
+
def to_dict(self) -> dict[str, Any]:
|
|
52
53
|
"""Convert metrics to dictionary."""
|
|
53
54
|
return {
|
|
54
55
|
"operation": self.operation_name,
|
|
55
56
|
"count": self.count,
|
|
56
57
|
"avg_duration_ms": round(self.avg_duration_ms, 2),
|
|
57
58
|
"min_duration_ms": (
|
|
58
|
-
round(self.min_duration_ms, 2)
|
|
59
|
-
if self.min_duration_ms != float("inf")
|
|
60
|
-
else 0.0
|
|
59
|
+
round(self.min_duration_ms, 2) if self.min_duration_ms != float("inf") else 0.0
|
|
61
60
|
),
|
|
62
61
|
"max_duration_ms": round(self.max_duration_ms, 2),
|
|
63
62
|
"error_count": self.error_count,
|
|
64
63
|
"error_rate_percent": round(self.error_rate, 2),
|
|
65
|
-
"last_execution": (
|
|
66
|
-
self.last_execution.isoformat() if self.last_execution else None
|
|
67
|
-
),
|
|
64
|
+
"last_execution": (self.last_execution.isoformat() if self.last_execution else None),
|
|
68
65
|
}
|
|
69
66
|
|
|
70
67
|
|
|
@@ -125,7 +122,7 @@ class MetricsCollector:
|
|
|
125
122
|
|
|
126
123
|
self._metrics[key].record(duration_ms, success)
|
|
127
124
|
|
|
128
|
-
def get_metrics(self, operation_name:
|
|
125
|
+
def get_metrics(self, operation_name: str | None = None) -> dict[str, Any]:
|
|
129
126
|
"""
|
|
130
127
|
Get metrics for operations.
|
|
131
128
|
|
|
@@ -138,9 +135,7 @@ class MetricsCollector:
|
|
|
138
135
|
with self._lock:
|
|
139
136
|
if operation_name:
|
|
140
137
|
metrics = {
|
|
141
|
-
k: v.to_dict()
|
|
142
|
-
for k, v in self._metrics.items()
|
|
143
|
-
if k.startswith(operation_name)
|
|
138
|
+
k: v.to_dict() for k, v in self._metrics.items() if k.startswith(operation_name)
|
|
144
139
|
}
|
|
145
140
|
# Move accessed metrics to end (LRU)
|
|
146
141
|
for key in list(self._metrics.keys()):
|
|
@@ -160,7 +155,7 @@ class MetricsCollector:
|
|
|
160
155
|
"total_operations": total_operations,
|
|
161
156
|
}
|
|
162
157
|
|
|
163
|
-
def get_summary(self) ->
|
|
158
|
+
def get_summary(self) -> dict[str, Any]:
|
|
164
159
|
"""
|
|
165
160
|
Get a summary of all metrics.
|
|
166
161
|
|
|
@@ -176,9 +171,9 @@ class MetricsCollector:
|
|
|
176
171
|
}
|
|
177
172
|
|
|
178
173
|
# Aggregate by base operation name (without tags)
|
|
179
|
-
aggregated:
|
|
174
|
+
aggregated: dict[str, OperationMetrics] = {}
|
|
180
175
|
|
|
181
|
-
for
|
|
176
|
+
for _key, metric in self._metrics.items():
|
|
182
177
|
base_name = metric.operation_name
|
|
183
178
|
if base_name not in aggregated:
|
|
184
179
|
aggregated[base_name] = OperationMetrics(operation_name=base_name)
|
|
@@ -223,7 +218,7 @@ class MetricsCollector:
|
|
|
223
218
|
|
|
224
219
|
|
|
225
220
|
# Global metrics collector instance
|
|
226
|
-
_metrics_collector:
|
|
221
|
+
_metrics_collector: MetricsCollector | None = None
|
|
227
222
|
|
|
228
223
|
|
|
229
224
|
def get_metrics_collector() -> MetricsCollector:
|
|
@@ -260,6 +255,32 @@ def timed_operation(operation_name: str, **tags: Any):
|
|
|
260
255
|
...
|
|
261
256
|
"""
|
|
262
257
|
|
|
258
|
+
# Common exceptions that indicate operation failure (not system-level exits)
|
|
259
|
+
# This is comprehensive to handle any decorated function's failures
|
|
260
|
+
_OPERATION_FAILURES = (
|
|
261
|
+
RuntimeError,
|
|
262
|
+
ValueError,
|
|
263
|
+
TypeError,
|
|
264
|
+
KeyError,
|
|
265
|
+
AttributeError,
|
|
266
|
+
IndexError,
|
|
267
|
+
LookupError,
|
|
268
|
+
IOError,
|
|
269
|
+
OSError,
|
|
270
|
+
ConnectionError,
|
|
271
|
+
TimeoutError,
|
|
272
|
+
PermissionError,
|
|
273
|
+
FileNotFoundError,
|
|
274
|
+
AssertionError,
|
|
275
|
+
ArithmeticError,
|
|
276
|
+
BufferError,
|
|
277
|
+
ImportError,
|
|
278
|
+
MemoryError,
|
|
279
|
+
StopIteration,
|
|
280
|
+
StopAsyncIteration,
|
|
281
|
+
UnicodeError,
|
|
282
|
+
)
|
|
283
|
+
|
|
263
284
|
def decorator(func: Callable) -> Callable:
|
|
264
285
|
if hasattr(func, "__code__") and func.__code__.co_flags & 0x80: # CO_COROUTINE
|
|
265
286
|
# Async function
|
|
@@ -269,7 +290,7 @@ def timed_operation(operation_name: str, **tags: Any):
|
|
|
269
290
|
try:
|
|
270
291
|
result = await func(*args, **kwargs)
|
|
271
292
|
return result
|
|
272
|
-
except
|
|
293
|
+
except _OPERATION_FAILURES:
|
|
273
294
|
success = False
|
|
274
295
|
raise
|
|
275
296
|
finally:
|
|
@@ -285,7 +306,7 @@ def timed_operation(operation_name: str, **tags: Any):
|
|
|
285
306
|
try:
|
|
286
307
|
result = func(*args, **kwargs)
|
|
287
308
|
return result
|
|
288
|
-
except
|
|
309
|
+
except _OPERATION_FAILURES:
|
|
289
310
|
success = False
|
|
290
311
|
raise
|
|
291
312
|
finally:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MDB Engine Repository Pattern
|
|
3
|
+
|
|
4
|
+
Provides abstract repository interfaces and MongoDB implementations
|
|
5
|
+
for clean data access patterns.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from mdb_engine.repositories import Repository, MongoRepository
|
|
9
|
+
|
|
10
|
+
# In domain services
|
|
11
|
+
class UserService:
|
|
12
|
+
def __init__(self, users: Repository[User]):
|
|
13
|
+
self._users = users
|
|
14
|
+
|
|
15
|
+
async def get_user(self, id: str) -> User:
|
|
16
|
+
return await self._users.get(id)
|
|
17
|
+
|
|
18
|
+
# In FastAPI routes using UnitOfWork
|
|
19
|
+
@app.get("/users/{user_id}")
|
|
20
|
+
async def get_user(user_id: str, ctx: RequestContext = Depends()):
|
|
21
|
+
user = await ctx.uow.users.get(user_id)
|
|
22
|
+
return user
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from .base import Entity, Repository
|
|
26
|
+
from .mongo import MongoRepository
|
|
27
|
+
from .unit_of_work import UnitOfWork
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Repository",
|
|
31
|
+
"Entity",
|
|
32
|
+
"MongoRepository",
|
|
33
|
+
"UnitOfWork",
|
|
34
|
+
]
|