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.
@@ -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