mdb-engine 0.1.6__py3-none-any.whl → 0.2.0__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 +104 -11
- mdb_engine/auth/ARCHITECTURE.md +112 -0
- mdb_engine/auth/README.md +648 -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 +264 -69
- mdb_engine/auth/config_helpers.py +7 -6
- mdb_engine/auth/cookie_utils.py +3 -7
- mdb_engine/auth/csrf.py +373 -0
- mdb_engine/auth/decorators.py +3 -10
- mdb_engine/auth/dependencies.py +47 -50
- mdb_engine/auth/helpers.py +3 -3
- mdb_engine/auth/integration.py +53 -80
- mdb_engine/auth/jwt.py +2 -6
- mdb_engine/auth/middleware.py +77 -34
- mdb_engine/auth/oso_factory.py +18 -38
- mdb_engine/auth/provider.py +270 -171
- mdb_engine/auth/rate_limiter.py +504 -0
- mdb_engine/auth/restrictions.py +8 -24
- mdb_engine/auth/session_manager.py +14 -29
- mdb_engine/auth/shared_middleware.py +600 -0
- mdb_engine/auth/shared_users.py +759 -0
- mdb_engine/auth/token_store.py +14 -28
- mdb_engine/auth/users.py +54 -113
- mdb_engine/auth/utils.py +213 -15
- mdb_engine/cli/commands/generate.py +545 -9
- mdb_engine/cli/commands/validate.py +3 -7
- mdb_engine/cli/utils.py +3 -3
- mdb_engine/config.py +7 -21
- 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 +22 -41
- mdb_engine/core/app_secrets.py +290 -0
- mdb_engine/core/connection.py +18 -9
- mdb_engine/core/encryption.py +223 -0
- mdb_engine/core/engine.py +1057 -93
- mdb_engine/core/index_management.py +12 -16
- mdb_engine/core/manifest.py +459 -150
- mdb_engine/core/ray_integration.py +435 -0
- mdb_engine/core/seeding.py +10 -18
- mdb_engine/core/service_initialization.py +12 -23
- mdb_engine/core/types.py +2 -5
- mdb_engine/database/README.md +140 -17
- mdb_engine/database/__init__.py +17 -6
- mdb_engine/database/abstraction.py +25 -37
- mdb_engine/database/connection.py +11 -18
- mdb_engine/database/query_validator.py +367 -0
- mdb_engine/database/resource_limiter.py +204 -0
- mdb_engine/database/scoped_wrapper.py +713 -196
- mdb_engine/dependencies.py +426 -0
- mdb_engine/di/__init__.py +34 -0
- mdb_engine/di/container.py +248 -0
- mdb_engine/di/providers.py +205 -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 +37 -154
- mdb_engine/embeddings/service.py +11 -25
- mdb_engine/exceptions.py +92 -0
- mdb_engine/indexes/README.md +30 -13
- mdb_engine/indexes/__init__.py +1 -0
- mdb_engine/indexes/helpers.py +1 -1
- mdb_engine/indexes/manager.py +50 -114
- mdb_engine/memory/README.md +2 -2
- mdb_engine/memory/__init__.py +1 -2
- mdb_engine/memory/service.py +30 -87
- mdb_engine/observability/README.md +4 -2
- mdb_engine/observability/__init__.py +26 -9
- mdb_engine/observability/health.py +8 -9
- mdb_engine/observability/metrics.py +32 -12
- 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 +25 -60
- mdb_engine-0.2.0.dist-info/METADATA +313 -0
- mdb_engine-0.2.0.dist-info/RECORD +96 -0
- 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.2.0.dist-info}/WHEEL +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/entry_points.txt +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/top_level.txt +0 -0
mdb_engine/constants.py
CHANGED
|
@@ -57,6 +57,25 @@ DEFAULT_MAX_IDLE_TIME_MS: Final[int] = 45000
|
|
|
57
57
|
MAX_COLLECTION_NAME_LENGTH: Final[int] = 255
|
|
58
58
|
"""Maximum length for MongoDB collection names."""
|
|
59
59
|
|
|
60
|
+
MIN_COLLECTION_NAME_LENGTH: Final[int] = 1
|
|
61
|
+
"""Minimum length for MongoDB collection names."""
|
|
62
|
+
|
|
63
|
+
# Reserved collection name prefixes (MongoDB system collections)
|
|
64
|
+
RESERVED_COLLECTION_PREFIXES: Final[tuple[str, ...]] = (
|
|
65
|
+
"system",
|
|
66
|
+
"admin",
|
|
67
|
+
"config",
|
|
68
|
+
"local",
|
|
69
|
+
)
|
|
70
|
+
"""Reserved MongoDB collection name prefixes that cannot be used."""
|
|
71
|
+
|
|
72
|
+
# Reserved collection names (engine-internal)
|
|
73
|
+
RESERVED_COLLECTION_NAMES: Final[tuple[str, ...]] = (
|
|
74
|
+
"apps_config", # Engine internal - app registration
|
|
75
|
+
"_mdb_engine_app_secrets", # Engine internal - encrypted app secrets
|
|
76
|
+
)
|
|
77
|
+
"""Reserved collection names that cannot be accessed through scoped wrappers."""
|
|
78
|
+
|
|
60
79
|
# App slug constraints
|
|
61
80
|
MAX_APP_SLUG_LENGTH: Final[int] = 100
|
|
62
81
|
"""Maximum length for app slugs."""
|
|
@@ -158,3 +177,49 @@ SUPPORTED_APP_STATUSES: Final[tuple[str, ...]] = (
|
|
|
158
177
|
APP_STATUS_ARCHIVED,
|
|
159
178
|
APP_STATUS_INACTIVE,
|
|
160
179
|
)
|
|
180
|
+
|
|
181
|
+
# ============================================================================
|
|
182
|
+
# QUERY SECURITY & RESOURCE LIMITS
|
|
183
|
+
# ============================================================================
|
|
184
|
+
|
|
185
|
+
# Query execution limits
|
|
186
|
+
DEFAULT_MAX_TIME_MS: Final[int] = 30000
|
|
187
|
+
"""Default query timeout in milliseconds (30 seconds)."""
|
|
188
|
+
|
|
189
|
+
MAX_QUERY_TIME_MS: Final[int] = 300000
|
|
190
|
+
"""Maximum allowed query timeout in milliseconds (5 minutes)."""
|
|
191
|
+
|
|
192
|
+
MAX_QUERY_RESULT_SIZE: Final[int] = 10000
|
|
193
|
+
"""Maximum number of documents that can be returned in a single query."""
|
|
194
|
+
|
|
195
|
+
MAX_CURSOR_BATCH_SIZE: Final[int] = 1000
|
|
196
|
+
"""Maximum batch size for cursor operations."""
|
|
197
|
+
|
|
198
|
+
MAX_DOCUMENT_SIZE: Final[int] = 16 * 1024 * 1024
|
|
199
|
+
"""Maximum document size in bytes (16MB MongoDB limit)."""
|
|
200
|
+
|
|
201
|
+
# Pipeline limits
|
|
202
|
+
MAX_PIPELINE_STAGES: Final[int] = 50
|
|
203
|
+
"""Maximum number of stages allowed in an aggregation pipeline."""
|
|
204
|
+
|
|
205
|
+
MAX_SORT_FIELDS: Final[int] = 10
|
|
206
|
+
"""Maximum number of fields that can be sorted in a single query."""
|
|
207
|
+
|
|
208
|
+
MAX_QUERY_DEPTH: Final[int] = 10
|
|
209
|
+
"""Maximum nesting depth for query filters (prevents deeply nested queries)."""
|
|
210
|
+
|
|
211
|
+
# Regex limits (prevent ReDoS attacks)
|
|
212
|
+
MAX_REGEX_LENGTH: Final[int] = 1000
|
|
213
|
+
"""Maximum length of regex patterns to prevent ReDoS attacks."""
|
|
214
|
+
|
|
215
|
+
MAX_REGEX_COMPLEXITY: Final[int] = 50
|
|
216
|
+
"""Maximum complexity score for regex patterns (prevents ReDoS)."""
|
|
217
|
+
|
|
218
|
+
# Dangerous MongoDB operators that should be blocked
|
|
219
|
+
DANGEROUS_OPERATORS: Final[tuple[str, ...]] = (
|
|
220
|
+
"$where", # JavaScript execution (security risk)
|
|
221
|
+
"$eval", # JavaScript evaluation (deprecated, security risk)
|
|
222
|
+
"$function", # JavaScript functions (security risk)
|
|
223
|
+
"$accumulator", # Can be abused for code execution
|
|
224
|
+
)
|
|
225
|
+
"""MongoDB operators that are blocked for security reasons."""
|
mdb_engine/core/README.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
The central orchestration engine for MDB_ENGINE that manages database connections, app registration, manifest validation, index management, and resource lifecycle.
|
|
4
4
|
|
|
5
|
+
## When to Use What
|
|
6
|
+
|
|
7
|
+
### Integration Patterns
|
|
8
|
+
|
|
9
|
+
| Pattern | Best For | Code |
|
|
10
|
+
|---------|----------|------|
|
|
11
|
+
| **`create_app()`** | New FastAPI apps | `app = engine.create_app(slug, manifest)` |
|
|
12
|
+
| **`lifespan()`** | Custom FastAPI setup | `FastAPI(lifespan=engine.lifespan(...))` |
|
|
13
|
+
| **Manual** | Existing apps, scripts | `await engine.initialize()` / `await engine.shutdown()` |
|
|
14
|
+
| **Context Manager** | Scripts, tests | `async with engine: ...` |
|
|
15
|
+
|
|
16
|
+
### Feature Activation
|
|
17
|
+
|
|
18
|
+
| Feature | When to Enable | Configuration |
|
|
19
|
+
|---------|---------------|---------------|
|
|
20
|
+
| **Ray** | Distributed processing, isolated actors | `enable_ray=True` |
|
|
21
|
+
| **Multi-site** | Cross-app data sharing | `read_scopes` in manifest |
|
|
22
|
+
| **Auto-indexing** | Let engine optimize queries | Default ON (`auto_index=True`) |
|
|
23
|
+
| **App Tokens** | Production security | Set `MDB_ENGINE_MASTER_KEY` |
|
|
24
|
+
|
|
5
25
|
## Features
|
|
6
26
|
|
|
7
27
|
- **MongoDBEngine**: Central orchestration for all engine components
|
|
@@ -54,12 +74,57 @@ engine = MongoDBEngine(
|
|
|
54
74
|
manifests_dir=Path("./manifests"), # Optional
|
|
55
75
|
authz_provider=authz_provider, # Optional
|
|
56
76
|
max_pool_size=10, # Optional
|
|
57
|
-
min_pool_size=1
|
|
77
|
+
min_pool_size=1, # Optional
|
|
78
|
+
enable_ray=False, # Optional: Enable Ray support
|
|
79
|
+
ray_namespace="modular_labs" # Optional: Ray namespace
|
|
58
80
|
)
|
|
59
81
|
|
|
60
82
|
await engine.initialize()
|
|
61
83
|
```
|
|
62
84
|
|
|
85
|
+
### FastAPI Integration
|
|
86
|
+
|
|
87
|
+
The simplest way to create a FastAPI app with automatic lifecycle management:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from mdb_engine import MongoDBEngine
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
|
|
93
|
+
engine = MongoDBEngine(
|
|
94
|
+
mongo_uri="mongodb://localhost:27017",
|
|
95
|
+
db_name="my_database"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Create FastAPI app with automatic initialization and cleanup
|
|
99
|
+
app = engine.create_app(
|
|
100
|
+
slug="my_app",
|
|
101
|
+
manifest=Path("manifest.json")
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@app.get("/items")
|
|
105
|
+
async def get_items():
|
|
106
|
+
db = engine.get_scoped_db("my_app")
|
|
107
|
+
return await db.items.find({}).to_list(10)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This automatically:
|
|
111
|
+
- Initializes the engine on startup
|
|
112
|
+
- Loads and registers the manifest
|
|
113
|
+
- Auto-detects multi-site mode from manifest
|
|
114
|
+
- Auto-retrieves app tokens
|
|
115
|
+
- Shuts down cleanly on app exit
|
|
116
|
+
|
|
117
|
+
### Custom Lifespan
|
|
118
|
+
|
|
119
|
+
For more control, use the `lifespan()` helper:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from fastapi import FastAPI
|
|
123
|
+
|
|
124
|
+
engine = MongoDBEngine(...)
|
|
125
|
+
app = FastAPI(lifespan=engine.lifespan("my_app", Path("manifest.json")))
|
|
126
|
+
```
|
|
127
|
+
|
|
63
128
|
### Get Scoped Database
|
|
64
129
|
|
|
65
130
|
Get a database wrapper that automatically isolates data by app:
|
|
@@ -91,6 +156,33 @@ manifest = await engine.load_manifest("manifest.json")
|
|
|
91
156
|
await engine.register_app(manifest)
|
|
92
157
|
```
|
|
93
158
|
|
|
159
|
+
### Optional Ray Integration
|
|
160
|
+
|
|
161
|
+
Enable Ray for distributed processing (Ray must be installed):
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from mdb_engine import MongoDBEngine
|
|
165
|
+
|
|
166
|
+
# Enable Ray support
|
|
167
|
+
engine = MongoDBEngine(
|
|
168
|
+
mongo_uri="mongodb://localhost:27017",
|
|
169
|
+
db_name="my_database",
|
|
170
|
+
enable_ray=True,
|
|
171
|
+
ray_namespace="my_namespace"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
await engine.initialize()
|
|
175
|
+
|
|
176
|
+
# Check if Ray is available
|
|
177
|
+
if engine.has_ray:
|
|
178
|
+
print(f"Ray initialized in namespace: {engine.ray_namespace}")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Ray integration features:
|
|
182
|
+
- Isolated app environments via Ray actors
|
|
183
|
+
- Automatic graceful degradation if Ray not installed
|
|
184
|
+
- App-specific namespaces for isolation
|
|
185
|
+
|
|
94
186
|
### Health Checks
|
|
95
187
|
|
|
96
188
|
Check engine and MongoDB health:
|
|
@@ -98,10 +190,13 @@ Check engine and MongoDB health:
|
|
|
98
190
|
```python
|
|
99
191
|
from mdb_engine.observability import check_engine_health, check_mongodb_health
|
|
100
192
|
|
|
101
|
-
# Check MongoDB connection
|
|
193
|
+
# Check MongoDB connection (mongo_client is for observability only, not data access)
|
|
102
194
|
mongodb_status = await check_mongodb_health(engine.mongo_client)
|
|
103
195
|
print(mongodb_status)
|
|
104
196
|
|
|
197
|
+
# ⚠️ SECURITY: engine.mongo_client is for observability/admin operations only
|
|
198
|
+
# Always use engine.get_scoped_db() for all data access operations
|
|
199
|
+
|
|
105
200
|
# Check engine health
|
|
106
201
|
engine_status = await check_engine_health(engine)
|
|
107
202
|
print(engine_status)
|
|
@@ -299,11 +394,23 @@ is_valid, error, paths = await validate_manifest_with_db(
|
|
|
299
394
|
- `get_app_info(app_slug)` - Get information about registered app
|
|
300
395
|
- `unregister_app(app_slug)` - Unregister an app
|
|
301
396
|
|
|
397
|
+
**FastAPI Integration Methods:**
|
|
398
|
+
|
|
399
|
+
- `create_app(slug, manifest, title=None, **fastapi_kwargs)` - Create a FastAPI app with automatic lifecycle management
|
|
400
|
+
- `lifespan(slug, manifest)` - Create a lifespan context manager for FastAPI
|
|
401
|
+
|
|
402
|
+
**App Token Methods:**
|
|
403
|
+
|
|
404
|
+
- `auto_retrieve_app_token(slug)` - Auto-retrieve app token from environment or database
|
|
405
|
+
- `get_app_token(slug)` - Get cached app token for a slug
|
|
406
|
+
|
|
302
407
|
#### Properties
|
|
303
408
|
|
|
304
|
-
- `mongo_client` - MongoDB client instance
|
|
305
|
-
- `mongo_db` - MongoDB database instance
|
|
409
|
+
- `mongo_client` - MongoDB client instance (for observability only)
|
|
306
410
|
- `initialized` - Whether engine is initialized
|
|
411
|
+
- `has_ray` - Whether Ray is enabled and initialized
|
|
412
|
+
- `enable_ray` - Whether Ray support is enabled
|
|
413
|
+
- `ray_namespace` - Ray namespace for actor isolation
|
|
307
414
|
|
|
308
415
|
### ManifestValidator
|
|
309
416
|
|
|
@@ -354,6 +461,8 @@ export MONGO_SERVER_SELECTION_TIMEOUT_MS=5000
|
|
|
354
461
|
- `authz_provider` (optional): Authorization provider instance
|
|
355
462
|
- `max_pool_size` (optional): Maximum connection pool size (default: 10)
|
|
356
463
|
- `min_pool_size` (optional): Minimum connection pool size (default: 1)
|
|
464
|
+
- `enable_ray` (optional): Enable Ray support for distributed processing (default: False)
|
|
465
|
+
- `ray_namespace` (optional): Ray namespace for actor isolation (default: "modular_labs")
|
|
357
466
|
|
|
358
467
|
## Manifest Schema Features
|
|
359
468
|
|
|
@@ -480,13 +589,15 @@ Define various index types:
|
|
|
480
589
|
```python
|
|
481
590
|
from mdb_engine.exceptions import InitializationError
|
|
482
591
|
|
|
592
|
+
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
|
|
593
|
+
|
|
483
594
|
try:
|
|
484
595
|
await engine.initialize()
|
|
485
596
|
except InitializationError as e:
|
|
486
597
|
print(f"Initialization failed: {e}")
|
|
487
598
|
print(f"MongoDB URI: {e.mongo_uri}")
|
|
488
|
-
except
|
|
489
|
-
print(f"
|
|
599
|
+
except (ConnectionFailure, ServerSelectionTimeoutError) as e:
|
|
600
|
+
print(f"MongoDB connection error: {e}")
|
|
490
601
|
```
|
|
491
602
|
|
|
492
603
|
## Integration Examples
|
mdb_engine/core/__init__.py
CHANGED
|
@@ -3,20 +3,52 @@ Core MongoDB Engine components.
|
|
|
3
3
|
|
|
4
4
|
This module contains the main MongoDBEngine class and core
|
|
5
5
|
orchestration logic for managing apps.
|
|
6
|
+
|
|
7
|
+
The MongoDBEngine now includes:
|
|
8
|
+
- FastAPI integration with create_app() method
|
|
9
|
+
- Optional Ray support with enable_ray parameter
|
|
10
|
+
- Automatic app token retrieval
|
|
11
|
+
- Multi-site mode auto-detection from manifest
|
|
6
12
|
"""
|
|
7
13
|
|
|
8
14
|
from .engine import MongoDBEngine
|
|
9
15
|
from .manifest import ( # Classes; Constants; Functions (for backward compatibility); Schemas
|
|
10
|
-
CURRENT_SCHEMA_VERSION,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
CURRENT_SCHEMA_VERSION,
|
|
17
|
+
DEFAULT_SCHEMA_VERSION,
|
|
18
|
+
MANIFEST_SCHEMA,
|
|
19
|
+
MANIFEST_SCHEMA_V1,
|
|
20
|
+
MANIFEST_SCHEMA_V2,
|
|
21
|
+
SCHEMA_REGISTRY,
|
|
22
|
+
ManifestParser,
|
|
23
|
+
ManifestValidator,
|
|
24
|
+
clear_validation_cache,
|
|
25
|
+
get_schema_for_version,
|
|
26
|
+
get_schema_version,
|
|
27
|
+
migrate_manifest,
|
|
28
|
+
validate_developer_id,
|
|
29
|
+
validate_index_definition,
|
|
30
|
+
validate_managed_indexes,
|
|
31
|
+
validate_manifest,
|
|
32
|
+
validate_manifest_with_db,
|
|
33
|
+
validate_manifests_parallel,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Optional Ray integration (gracefully handles missing Ray)
|
|
37
|
+
from .ray_integration import (
|
|
38
|
+
RAY_AVAILABLE,
|
|
39
|
+
AppRayActor,
|
|
40
|
+
get_ray_actor_handle,
|
|
41
|
+
ray_actor_decorator,
|
|
42
|
+
)
|
|
16
43
|
|
|
17
44
|
__all__ = [
|
|
18
|
-
# MongoDB Engine
|
|
45
|
+
# MongoDB Engine (includes FastAPI integration and optional Ray)
|
|
19
46
|
"MongoDBEngine",
|
|
47
|
+
# Ray Integration (optional - only active if Ray installed)
|
|
48
|
+
"RAY_AVAILABLE",
|
|
49
|
+
"AppRayActor",
|
|
50
|
+
"get_ray_actor_handle",
|
|
51
|
+
"ray_actor_decorator",
|
|
20
52
|
# Classes
|
|
21
53
|
"ManifestValidator",
|
|
22
54
|
"ManifestParser",
|
|
@@ -13,13 +13,18 @@ import time
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
|
|
15
15
|
|
|
16
|
+
from jsonschema import SchemaError
|
|
17
|
+
from jsonschema import ValidationError as JsonSchemaValidationError
|
|
16
18
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
|
17
|
-
from pymongo.errors import (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
from pymongo.errors import (
|
|
20
|
+
ConnectionFailure,
|
|
21
|
+
InvalidOperation,
|
|
22
|
+
OperationFailure,
|
|
23
|
+
ServerSelectionTimeoutError,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from ..observability import clear_app_context, record_operation, set_app_context
|
|
21
27
|
from ..observability import get_logger as get_contextual_logger
|
|
22
|
-
from ..observability import record_operation, set_app_context
|
|
23
28
|
from .manifest import ManifestParser, ManifestValidator
|
|
24
29
|
|
|
25
30
|
if TYPE_CHECKING:
|
|
@@ -81,7 +86,7 @@ class AppRegistrationManager:
|
|
|
81
86
|
experiment_slug=slug,
|
|
82
87
|
)
|
|
83
88
|
return result
|
|
84
|
-
except
|
|
89
|
+
except (JsonSchemaValidationError, SchemaError, ValueError, TypeError, KeyError):
|
|
85
90
|
duration_ms = (time.time() - start_time) * 1000
|
|
86
91
|
record_operation(
|
|
87
92
|
"app_registration.validate_manifest",
|
|
@@ -111,15 +116,9 @@ class AppRegistrationManager:
|
|
|
111
116
|
self,
|
|
112
117
|
manifest: "ManifestDict",
|
|
113
118
|
create_indexes_callback: Optional[Callable[[str, "ManifestDict"], Any]] = None,
|
|
114
|
-
seed_data_callback: Optional[
|
|
115
|
-
|
|
116
|
-
] = None,
|
|
117
|
-
initialize_memory_callback: Optional[
|
|
118
|
-
Callable[[str, Dict[str, Any]], Any]
|
|
119
|
-
] = None,
|
|
120
|
-
register_websockets_callback: Optional[
|
|
121
|
-
Callable[[str, Dict[str, Any]], Any]
|
|
122
|
-
] = None,
|
|
119
|
+
seed_data_callback: Optional[Callable[[str, Dict[str, List[Dict[str, Any]]]], Any]] = None,
|
|
120
|
+
initialize_memory_callback: Optional[Callable[[str, Dict[str, Any]], Any]] = None,
|
|
121
|
+
register_websockets_callback: Optional[Callable[[str, Dict[str, Any]], Any]] = None,
|
|
123
122
|
setup_observability_callback: Optional[
|
|
124
123
|
Callable[[str, "ManifestDict", Dict[str, Any]], Any]
|
|
125
124
|
] = None,
|
|
@@ -210,9 +209,7 @@ class AppRegistrationManager:
|
|
|
210
209
|
)
|
|
211
210
|
# Continue even if persistence fails - app is still registered in memory
|
|
212
211
|
except InvalidOperation as e:
|
|
213
|
-
logger.debug(
|
|
214
|
-
f"Cannot persist app '{slug}': MongoDB client is closed: {e}"
|
|
215
|
-
)
|
|
212
|
+
logger.debug(f"Cannot persist app '{slug}': MongoDB client is closed: {e}")
|
|
216
213
|
# Continue - app is still registered in memory
|
|
217
214
|
|
|
218
215
|
# Invalidate auth config cache for this app
|
|
@@ -228,32 +225,22 @@ class AppRegistrationManager:
|
|
|
228
225
|
|
|
229
226
|
# Create indexes if requested
|
|
230
227
|
if create_indexes_callback and "managed_indexes" in manifest:
|
|
231
|
-
logger.info(
|
|
232
|
-
f"[{slug}] Creating managed indexes " f"(has_managed_indexes=True)"
|
|
233
|
-
)
|
|
228
|
+
logger.info(f"[{slug}] Creating managed indexes " f"(has_managed_indexes=True)")
|
|
234
229
|
callback_tasks.append(create_indexes_callback(slug, manifest))
|
|
235
230
|
|
|
236
231
|
# Seed initial data if configured
|
|
237
232
|
if seed_data_callback and "initial_data" in manifest:
|
|
238
|
-
callback_tasks.append(
|
|
239
|
-
seed_data_callback(slug, manifest["initial_data"])
|
|
240
|
-
)
|
|
233
|
+
callback_tasks.append(seed_data_callback(slug, manifest["initial_data"]))
|
|
241
234
|
|
|
242
235
|
# Initialize Memory service if configured
|
|
243
236
|
memory_config = manifest.get("memory_config")
|
|
244
|
-
if (
|
|
245
|
-
initialize_memory_callback
|
|
246
|
-
and memory_config
|
|
247
|
-
and memory_config.get("enabled", False)
|
|
248
|
-
):
|
|
237
|
+
if initialize_memory_callback and memory_config and memory_config.get("enabled", False):
|
|
249
238
|
callback_tasks.append(initialize_memory_callback(slug, memory_config))
|
|
250
239
|
|
|
251
240
|
# Register WebSocket endpoints if configured
|
|
252
241
|
websockets_config = manifest.get("websockets")
|
|
253
242
|
if register_websockets_callback and websockets_config:
|
|
254
|
-
callback_tasks.append(
|
|
255
|
-
register_websockets_callback(slug, websockets_config)
|
|
256
|
-
)
|
|
243
|
+
callback_tasks.append(register_websockets_callback(slug, websockets_config))
|
|
257
244
|
|
|
258
245
|
# Set up observability (health checks, metrics, logging)
|
|
259
246
|
observability_config = manifest.get("observability", {})
|
|
@@ -275,9 +262,7 @@ class AppRegistrationManager:
|
|
|
275
262
|
"register_websockets",
|
|
276
263
|
"setup_observability",
|
|
277
264
|
]
|
|
278
|
-
callback_name = (
|
|
279
|
-
callback_names[i] if i < len(callback_names) else "unknown"
|
|
280
|
-
)
|
|
265
|
+
callback_name = callback_names[i] if i < len(callback_names) else "unknown"
|
|
281
266
|
logger.warning(
|
|
282
267
|
f"[{slug}] Callback '{callback_name}' failed during "
|
|
283
268
|
f"app registration: {result}",
|
|
@@ -295,9 +280,7 @@ class AppRegistrationManager:
|
|
|
295
280
|
"App registered successfully",
|
|
296
281
|
extra={
|
|
297
282
|
"app_slug": slug,
|
|
298
|
-
"memory_enabled": bool(
|
|
299
|
-
memory_config and memory_config.get("enabled", False)
|
|
300
|
-
),
|
|
283
|
+
"memory_enabled": bool(memory_config and memory_config.get("enabled", False)),
|
|
301
284
|
"websockets_configured": bool(websockets_config),
|
|
302
285
|
"duration_ms": round(duration_ms, 2),
|
|
303
286
|
},
|
|
@@ -324,9 +307,7 @@ class AppRegistrationManager:
|
|
|
324
307
|
try:
|
|
325
308
|
# Fetch active apps
|
|
326
309
|
active_cfgs = (
|
|
327
|
-
await self._mongo_db.apps_config.find({"status": "active"})
|
|
328
|
-
.limit(500)
|
|
329
|
-
.to_list(None)
|
|
310
|
+
await self._mongo_db.apps_config.find({"status": "active"}).limit(500).to_list(None)
|
|
330
311
|
)
|
|
331
312
|
|
|
332
313
|
logger.info(f"Found {len(active_cfgs)} active app(s).")
|