dominus-sdk-python-dev 2.1.8__py3-none-any.whl → 2.3.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.
- dominus/__init__.py +80 -19
- dominus/config/__init__.py +4 -4
- dominus/config/endpoints.py +18 -5
- dominus/namespaces/admin.py +58 -0
- dominus/namespaces/auth.py +539 -10
- dominus/namespaces/db.py +26 -1
- dominus/namespaces/ddl.py +222 -0
- dominus/namespaces/fastapi.py +252 -0
- dominus/namespaces/files.py +180 -0
- dominus/namespaces/secure.py +256 -0
- dominus/start.py +22 -0
- {dominus_sdk_python_dev-2.1.8.dist-info → dominus_sdk_python_dev-2.3.0.dist-info}/METADATA +215 -28
- {dominus_sdk_python_dev-2.1.8.dist-info → dominus_sdk_python_dev-2.3.0.dist-info}/RECORD +15 -12
- {dominus_sdk_python_dev-2.1.8.dist-info → dominus_sdk_python_dev-2.3.0.dist-info}/WHEEL +0 -0
- {dominus_sdk_python_dev-2.1.8.dist-info → dominus_sdk_python_dev-2.3.0.dist-info}/top_level.txt +0 -0
dominus/namespaces/files.py
CHANGED
|
@@ -297,3 +297,183 @@ class FilesNamespace:
|
|
|
297
297
|
endpoint="/api/archivist/update",
|
|
298
298
|
body=body
|
|
299
299
|
)
|
|
300
|
+
|
|
301
|
+
# ========================================
|
|
302
|
+
# STORAGE BROWSER METHODS
|
|
303
|
+
# ========================================
|
|
304
|
+
|
|
305
|
+
async def browse(
|
|
306
|
+
self,
|
|
307
|
+
path: str = "/",
|
|
308
|
+
category: Optional[str] = None,
|
|
309
|
+
view: Optional[str] = None,
|
|
310
|
+
limit: int = 100,
|
|
311
|
+
cursor: Optional[str] = None
|
|
312
|
+
) -> Dict[str, Any]:
|
|
313
|
+
"""
|
|
314
|
+
Browse files and folders at a path.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
path: Directory path to browse (default: "/")
|
|
318
|
+
category: Filter by category
|
|
319
|
+
view: View name (e.g., "all", "compliance")
|
|
320
|
+
limit: Maximum items to return (default: 100)
|
|
321
|
+
cursor: Pagination cursor
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Dict with "path", "folders", "files", and optional "cursor"
|
|
325
|
+
"""
|
|
326
|
+
body = {
|
|
327
|
+
"path": path,
|
|
328
|
+
"limit": limit
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if category:
|
|
332
|
+
body["category"] = category
|
|
333
|
+
if view:
|
|
334
|
+
body["view"] = view
|
|
335
|
+
if cursor:
|
|
336
|
+
body["cursor"] = cursor
|
|
337
|
+
|
|
338
|
+
return await self._client._request(
|
|
339
|
+
endpoint="/api/archivist/browse",
|
|
340
|
+
body=body
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
async def get_storage_stats(self) -> Dict[str, Any]:
|
|
344
|
+
"""
|
|
345
|
+
Get storage statistics.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Dict with "total_objects", "total_size_bytes", "by_category"
|
|
349
|
+
"""
|
|
350
|
+
return await self._client._request(
|
|
351
|
+
endpoint="/api/archivist/stats",
|
|
352
|
+
method="GET"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
async def list_categories(self) -> Dict[str, Any]:
|
|
356
|
+
"""
|
|
357
|
+
List all file categories.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Dict with "categories" list containing name, object_count, total_size_bytes
|
|
361
|
+
"""
|
|
362
|
+
return await self._client._request(
|
|
363
|
+
endpoint="/api/archivist/categories",
|
|
364
|
+
method="GET"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
async def list_views(self, include_reserved: bool = False) -> Dict[str, Any]:
|
|
368
|
+
"""
|
|
369
|
+
List available views.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
include_reserved: Include reserved system views (default: False)
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Dict with "views" list, "total_views", "is_admin"
|
|
376
|
+
"""
|
|
377
|
+
body = {"include_reserved": include_reserved}
|
|
378
|
+
|
|
379
|
+
return await self._client._request(
|
|
380
|
+
endpoint="/api/archivist/views",
|
|
381
|
+
body=body
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
async def list_categories_in_view(self, view: str) -> Dict[str, Any]:
|
|
385
|
+
"""
|
|
386
|
+
List categories within a specific view.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
view: View name
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Dict with "categories" list
|
|
393
|
+
"""
|
|
394
|
+
return await self._client._request(
|
|
395
|
+
endpoint=f"/api/archivist/views/{view}/categories",
|
|
396
|
+
method="GET"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
async def create_folder(
|
|
400
|
+
self,
|
|
401
|
+
path: str,
|
|
402
|
+
category: Optional[str] = None
|
|
403
|
+
) -> Dict[str, Any]:
|
|
404
|
+
"""
|
|
405
|
+
Create a folder.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
path: Folder path to create
|
|
409
|
+
category: Category for the folder (optional)
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Dict with "created", "path", optional "placeholder_id", "message"
|
|
413
|
+
"""
|
|
414
|
+
body = {"path": path}
|
|
415
|
+
|
|
416
|
+
if category:
|
|
417
|
+
body["category"] = category
|
|
418
|
+
|
|
419
|
+
return await self._client._request(
|
|
420
|
+
endpoint="/api/archivist/folder/create",
|
|
421
|
+
body=body
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
async def delete_folder(
|
|
425
|
+
self,
|
|
426
|
+
path: str,
|
|
427
|
+
category: Optional[str] = None,
|
|
428
|
+
force: bool = False
|
|
429
|
+
) -> Dict[str, Any]:
|
|
430
|
+
"""
|
|
431
|
+
Delete a folder.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
path: Folder path to delete
|
|
435
|
+
category: Category of the folder (optional)
|
|
436
|
+
force: Force delete non-empty folders (default: False)
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
Dict with "deleted", "path", "deleted_count", etc.
|
|
440
|
+
"""
|
|
441
|
+
body = {
|
|
442
|
+
"path": path,
|
|
443
|
+
"force": force
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if category:
|
|
447
|
+
body["category"] = category
|
|
448
|
+
|
|
449
|
+
return await self._client._request(
|
|
450
|
+
endpoint="/api/archivist/folder/delete",
|
|
451
|
+
body=body
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
async def factory_reset(
|
|
455
|
+
self,
|
|
456
|
+
confirm: bool = False,
|
|
457
|
+
preserve_compliance: bool = True
|
|
458
|
+
) -> Dict[str, Any]:
|
|
459
|
+
"""
|
|
460
|
+
Factory reset storage (delete all non-compliance files).
|
|
461
|
+
|
|
462
|
+
WARNING: This is a destructive operation.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
confirm: Must be True to proceed (safety check)
|
|
466
|
+
preserve_compliance: Keep compliance files (default: True)
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
Dict with "reset", "deleted_count", "retained_count", "by_category"
|
|
470
|
+
"""
|
|
471
|
+
body = {
|
|
472
|
+
"confirm": confirm,
|
|
473
|
+
"preserve_compliance": preserve_compliance
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return await self._client._request(
|
|
477
|
+
endpoint="/api/archivist/factory-reset",
|
|
478
|
+
body=body
|
|
479
|
+
)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Secure Namespace - Audit-logged data access for sensitive tables.
|
|
3
|
+
|
|
4
|
+
Provides CRUD operations with mandatory reason/actor parameters for compliance.
|
|
5
|
+
"""
|
|
6
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..start import Dominus
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecureAccessContext:
|
|
13
|
+
"""Access context for secure table operations."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, reason: str, actor: str):
|
|
16
|
+
self.reason = reason
|
|
17
|
+
self.actor = actor
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SecureNamespace:
|
|
21
|
+
"""
|
|
22
|
+
Secure data access namespace.
|
|
23
|
+
|
|
24
|
+
All operations require a SecureAccessContext with reason and actor
|
|
25
|
+
for HIPAA/compliance audit logging.
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
context = SecureAccessContext(
|
|
29
|
+
reason="Viewing patient chart for appointment #123",
|
|
30
|
+
actor="user:abc123"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# List secure tables
|
|
34
|
+
tables = await dominus.secure.tables(schema="tenant_acme")
|
|
35
|
+
|
|
36
|
+
# Query with audit context
|
|
37
|
+
patients = await dominus.secure.query(
|
|
38
|
+
"patients",
|
|
39
|
+
context,
|
|
40
|
+
filters={"active": True}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Insert with audit context
|
|
44
|
+
await dominus.secure.insert(
|
|
45
|
+
"patients",
|
|
46
|
+
{"mrn": "12345", "name": "John Doe"},
|
|
47
|
+
context
|
|
48
|
+
)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, client: "Dominus"):
|
|
52
|
+
self._client = client
|
|
53
|
+
|
|
54
|
+
async def tables(self, schema: str = "public") -> List[Dict[str, Any]]:
|
|
55
|
+
"""
|
|
56
|
+
List tables in a schema (secure view).
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
schema: Schema name (default: "public")
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
List of table metadata
|
|
63
|
+
"""
|
|
64
|
+
result = await self._client._request(
|
|
65
|
+
endpoint=f"/api/scribe/data/{schema}/tables",
|
|
66
|
+
method="GET"
|
|
67
|
+
)
|
|
68
|
+
return result.get("tables", result) if isinstance(result, dict) else result
|
|
69
|
+
|
|
70
|
+
async def columns(self, table: str, schema: str = "public") -> List[Dict[str, Any]]:
|
|
71
|
+
"""
|
|
72
|
+
List columns in a table (secure view).
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
table: Table name
|
|
76
|
+
schema: Schema name (default: "public")
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
List of column metadata with is_primary_key
|
|
80
|
+
"""
|
|
81
|
+
result = await self._client._request(
|
|
82
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/columns",
|
|
83
|
+
method="GET"
|
|
84
|
+
)
|
|
85
|
+
return result.get("columns", result) if isinstance(result, dict) else result
|
|
86
|
+
|
|
87
|
+
async def query(
|
|
88
|
+
self,
|
|
89
|
+
table: str,
|
|
90
|
+
context: SecureAccessContext,
|
|
91
|
+
schema: str = "public",
|
|
92
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
93
|
+
sort_by: Optional[str] = None,
|
|
94
|
+
sort_order: str = "ASC",
|
|
95
|
+
limit: int = 100,
|
|
96
|
+
offset: int = 0
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
"""
|
|
99
|
+
Query table data with mandatory audit context.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
table: Table name
|
|
103
|
+
context: SecureAccessContext with reason and actor
|
|
104
|
+
schema: Schema name (default: "public")
|
|
105
|
+
filters: Column:value filter dictionary
|
|
106
|
+
sort_by: Column to sort by
|
|
107
|
+
sort_order: "ASC" or "DESC"
|
|
108
|
+
limit: Maximum rows to return (default: 100)
|
|
109
|
+
offset: Rows to skip (default: 0)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dict with "rows" and "total" keys
|
|
113
|
+
"""
|
|
114
|
+
body = {
|
|
115
|
+
"filters": filters,
|
|
116
|
+
"sort_by": sort_by,
|
|
117
|
+
"sort_order": sort_order,
|
|
118
|
+
"limit": limit,
|
|
119
|
+
"offset": offset,
|
|
120
|
+
"reason": context.reason,
|
|
121
|
+
"actor": context.actor
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
result = await self._client._request(
|
|
125
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/query",
|
|
126
|
+
body=body
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Normalize response
|
|
130
|
+
return {
|
|
131
|
+
"rows": result.get("items", result.get("rows", [])),
|
|
132
|
+
"total": result.get("total", 0)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async def insert(
|
|
136
|
+
self,
|
|
137
|
+
table: str,
|
|
138
|
+
data: Dict[str, Any],
|
|
139
|
+
context: SecureAccessContext,
|
|
140
|
+
schema: str = "public"
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
"""
|
|
143
|
+
Insert a row with mandatory audit context.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
table: Table name
|
|
147
|
+
data: Column:value dictionary
|
|
148
|
+
context: SecureAccessContext with reason and actor
|
|
149
|
+
schema: Schema name (default: "public")
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Inserted row data
|
|
153
|
+
"""
|
|
154
|
+
body = {
|
|
155
|
+
"data": data,
|
|
156
|
+
"reason": context.reason,
|
|
157
|
+
"actor": context.actor
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return await self._client._request(
|
|
161
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/insert",
|
|
162
|
+
body=body
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
async def update(
|
|
166
|
+
self,
|
|
167
|
+
table: str,
|
|
168
|
+
data: Dict[str, Any],
|
|
169
|
+
filters: Dict[str, Any],
|
|
170
|
+
context: SecureAccessContext,
|
|
171
|
+
schema: str = "public"
|
|
172
|
+
) -> Dict[str, Any]:
|
|
173
|
+
"""
|
|
174
|
+
Update rows with mandatory audit context.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
table: Table name
|
|
178
|
+
data: Column:value dictionary of updates
|
|
179
|
+
filters: Column:value dictionary for WHERE clause
|
|
180
|
+
context: SecureAccessContext with reason and actor
|
|
181
|
+
schema: Schema name (default: "public")
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Dict with "affected_rows" count
|
|
185
|
+
"""
|
|
186
|
+
body = {
|
|
187
|
+
"data": data,
|
|
188
|
+
"filters": filters,
|
|
189
|
+
"reason": context.reason,
|
|
190
|
+
"actor": context.actor
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return await self._client._request(
|
|
194
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/update",
|
|
195
|
+
body=body
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
async def delete(
|
|
199
|
+
self,
|
|
200
|
+
table: str,
|
|
201
|
+
filters: Dict[str, Any],
|
|
202
|
+
context: SecureAccessContext,
|
|
203
|
+
schema: str = "public"
|
|
204
|
+
) -> Dict[str, Any]:
|
|
205
|
+
"""
|
|
206
|
+
Delete rows with mandatory audit context.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
table: Table name
|
|
210
|
+
filters: Column:value dictionary for WHERE clause
|
|
211
|
+
context: SecureAccessContext with reason and actor
|
|
212
|
+
schema: Schema name (default: "public")
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Dict with "affected_rows" count
|
|
216
|
+
"""
|
|
217
|
+
body = {
|
|
218
|
+
"filters": filters,
|
|
219
|
+
"reason": context.reason,
|
|
220
|
+
"actor": context.actor
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return await self._client._request(
|
|
224
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/delete",
|
|
225
|
+
body=body
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
async def bulk_insert(
|
|
229
|
+
self,
|
|
230
|
+
table: str,
|
|
231
|
+
rows: List[Dict[str, Any]],
|
|
232
|
+
context: SecureAccessContext,
|
|
233
|
+
schema: str = "public"
|
|
234
|
+
) -> Dict[str, Any]:
|
|
235
|
+
"""
|
|
236
|
+
Insert multiple rows with mandatory audit context.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
table: Table name
|
|
240
|
+
rows: List of column:value dictionaries
|
|
241
|
+
context: SecureAccessContext with reason and actor
|
|
242
|
+
schema: Schema name (default: "public")
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Dict with "inserted_count" and optionally "rows"
|
|
246
|
+
"""
|
|
247
|
+
body = {
|
|
248
|
+
"rows": rows,
|
|
249
|
+
"reason": context.reason,
|
|
250
|
+
"actor": context.actor
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return await self._client._request(
|
|
254
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/bulk-insert",
|
|
255
|
+
body=body
|
|
256
|
+
)
|
dominus/start.py
CHANGED
|
@@ -333,6 +333,28 @@ class Dominus:
|
|
|
333
333
|
from .namespaces.portal import PortalNamespace
|
|
334
334
|
self.portal = PortalNamespace(self)
|
|
335
335
|
|
|
336
|
+
# Initialize FastAPI namespace (decorators for route auth)
|
|
337
|
+
from .namespaces.fastapi import FastAPINamespace
|
|
338
|
+
self.fastapi = FastAPINamespace(self)
|
|
339
|
+
|
|
340
|
+
# Initialize full namespaces (matching Node.js SDK pattern)
|
|
341
|
+
from .namespaces.db import DbNamespace
|
|
342
|
+
from .namespaces.auth import AuthNamespace
|
|
343
|
+
from .namespaces.ddl import DdlNamespace
|
|
344
|
+
from .namespaces.files import FilesNamespace
|
|
345
|
+
from .namespaces.admin import AdminNamespace
|
|
346
|
+
from .namespaces.secure import SecureNamespace as SecureNS
|
|
347
|
+
|
|
348
|
+
self.db = DbNamespace(self)
|
|
349
|
+
self.auth = AuthNamespace(self)
|
|
350
|
+
self.ddl = DdlNamespace(self)
|
|
351
|
+
self.files = FilesNamespace(self)
|
|
352
|
+
self.admin = AdminNamespace(self)
|
|
353
|
+
|
|
354
|
+
# Note: self.secure is already defined above as the flat API namespace
|
|
355
|
+
# The proper SecureNS namespace is available as dominus.db_secure for direct access
|
|
356
|
+
self.db_secure = SecureNS(self)
|
|
357
|
+
|
|
336
358
|
# Cache for JWT public key
|
|
337
359
|
self._public_key_cache = None
|
|
338
360
|
|