dominus-sdk-python-dev 2.1.7__py3-none-any.whl → 2.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.
- dominus/__init__.py +80 -19
- dominus/config/__init__.py +4 -4
- dominus/namespaces/admin.py +58 -0
- dominus/namespaces/auth.py +539 -10
- dominus/namespaces/db.py +26 -1
- dominus/namespaces/ddl.py +206 -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.7.dist-info → dominus_sdk_python_dev-2.2.0.dist-info}/METADATA +215 -28
- {dominus_sdk_python_dev-2.1.7.dist-info → dominus_sdk_python_dev-2.2.0.dist-info}/RECORD +14 -11
- {dominus_sdk_python_dev-2.1.7.dist-info → dominus_sdk_python_dev-2.2.0.dist-info}/WHEEL +0 -0
- {dominus_sdk_python_dev-2.1.7.dist-info → dominus_sdk_python_dev-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|