dominus-sdk-python 2.0.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 +47 -0
- dominus/config/__init__.py +24 -0
- dominus/config/endpoints.py +43 -0
- dominus/errors.py +201 -0
- dominus/helpers/__init__.py +2 -0
- dominus/helpers/auth.py +49 -0
- dominus/helpers/cache.py +192 -0
- dominus/helpers/core.py +603 -0
- dominus/helpers/crypto.py +118 -0
- dominus/namespaces/__init__.py +26 -0
- dominus/namespaces/_deprecated_crossover.py +10 -0
- dominus/namespaces/_deprecated_sql.py +1341 -0
- dominus/namespaces/auth.py +1025 -0
- dominus/namespaces/courier.py +251 -0
- dominus/namespaces/db.py +267 -0
- dominus/namespaces/ddl.py +590 -0
- dominus/namespaces/files.py +299 -0
- dominus/namespaces/health.py +59 -0
- dominus/namespaces/logs.py +367 -0
- dominus/namespaces/open.py +72 -0
- dominus/namespaces/portal.py +437 -0
- dominus/namespaces/redis.py +357 -0
- dominus/namespaces/secrets.py +126 -0
- dominus/services/__init__.py +2 -0
- dominus/services/_deprecated_architect.py +323 -0
- dominus/services/_deprecated_sovereign.py +93 -0
- dominus/start.py +942 -0
- dominus_sdk_python-2.0.0.dist-info/METADATA +381 -0
- dominus_sdk_python-2.0.0.dist-info/RECORD +31 -0
- dominus_sdk_python-2.0.0.dist-info/WHEEL +5 -0
- dominus_sdk_python-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Courier Namespace - Email delivery via Postmark.
|
|
3
|
+
|
|
4
|
+
Provides email sending using Postmark templates.
|
|
5
|
+
"""
|
|
6
|
+
from typing import Any, Dict, Optional, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..start import Dominus
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CourierNamespace:
|
|
13
|
+
"""
|
|
14
|
+
Email delivery namespace.
|
|
15
|
+
|
|
16
|
+
All email operations go through /api/courier/* endpoints.
|
|
17
|
+
Uses Postmark for transactional email delivery.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
# Send welcome email
|
|
21
|
+
result = await dominus.courier.send(
|
|
22
|
+
template_alias="welcome",
|
|
23
|
+
to="user@example.com",
|
|
24
|
+
from_email="noreply@myapp.com",
|
|
25
|
+
model={
|
|
26
|
+
"name": "John Smith",
|
|
27
|
+
"product_name": "My App"
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Send with tracking tag
|
|
32
|
+
result = await dominus.courier.send(
|
|
33
|
+
template_alias="password-reset",
|
|
34
|
+
to="user@example.com",
|
|
35
|
+
from_email="noreply@myapp.com",
|
|
36
|
+
model={"reset_link": "https://..."},
|
|
37
|
+
tag="password-reset"
|
|
38
|
+
)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, client: "Dominus"):
|
|
42
|
+
self._client = client
|
|
43
|
+
|
|
44
|
+
async def send(
|
|
45
|
+
self,
|
|
46
|
+
template_alias: str,
|
|
47
|
+
to: str,
|
|
48
|
+
from_email: str,
|
|
49
|
+
model: Dict[str, Any],
|
|
50
|
+
tag: Optional[str] = None,
|
|
51
|
+
reply_to: Optional[str] = None
|
|
52
|
+
) -> Dict[str, Any]:
|
|
53
|
+
"""
|
|
54
|
+
Send email using Postmark template.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
template_alias: Template alias in Postmark (e.g., "welcome", "password-reset")
|
|
58
|
+
to: Recipient email address
|
|
59
|
+
from_email: Sender email address (must be verified in Postmark)
|
|
60
|
+
model: Template model variables (dict of key-value pairs)
|
|
61
|
+
tag: Optional tag for tracking/filtering in Postmark
|
|
62
|
+
reply_to: Optional reply-to address
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Dict with:
|
|
66
|
+
- to: Recipient email
|
|
67
|
+
- message_id: Postmark message UUID
|
|
68
|
+
- submitted_at: Timestamp
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
result = await dominus.courier.send(
|
|
72
|
+
template_alias="welcome",
|
|
73
|
+
to="john@example.com",
|
|
74
|
+
from_email="hello@myapp.com",
|
|
75
|
+
model={
|
|
76
|
+
"name": "John",
|
|
77
|
+
"company_name": "My App",
|
|
78
|
+
"action_url": "https://myapp.com/verify?token=abc123"
|
|
79
|
+
},
|
|
80
|
+
tag="welcome"
|
|
81
|
+
)
|
|
82
|
+
print(f"Email sent: {result['message_id']}")
|
|
83
|
+
"""
|
|
84
|
+
body = {
|
|
85
|
+
"template_alias": template_alias,
|
|
86
|
+
"to": to,
|
|
87
|
+
"from": from_email,
|
|
88
|
+
"model": model
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if tag:
|
|
92
|
+
body["tag"] = tag
|
|
93
|
+
if reply_to:
|
|
94
|
+
body["reply_to"] = reply_to
|
|
95
|
+
|
|
96
|
+
return await self._client._request(
|
|
97
|
+
endpoint="/api/courier/send",
|
|
98
|
+
body=body
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Convenience methods for common templates
|
|
102
|
+
|
|
103
|
+
async def send_welcome(
|
|
104
|
+
self,
|
|
105
|
+
to: str,
|
|
106
|
+
from_email: str,
|
|
107
|
+
name: str,
|
|
108
|
+
product_name: str,
|
|
109
|
+
action_url: Optional[str] = None,
|
|
110
|
+
**extra_model
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""
|
|
113
|
+
Send welcome email.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
to: Recipient email
|
|
117
|
+
from_email: Sender email
|
|
118
|
+
name: User's name
|
|
119
|
+
product_name: Your product/app name
|
|
120
|
+
action_url: Optional action button URL
|
|
121
|
+
**extra_model: Additional template variables
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Send result dict
|
|
125
|
+
"""
|
|
126
|
+
model = {
|
|
127
|
+
"name": name,
|
|
128
|
+
"product_name": product_name,
|
|
129
|
+
**extra_model
|
|
130
|
+
}
|
|
131
|
+
if action_url:
|
|
132
|
+
model["action_url"] = action_url
|
|
133
|
+
|
|
134
|
+
return await self.send(
|
|
135
|
+
template_alias="welcome",
|
|
136
|
+
to=to,
|
|
137
|
+
from_email=from_email,
|
|
138
|
+
model=model,
|
|
139
|
+
tag="welcome"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
async def send_password_reset(
|
|
143
|
+
self,
|
|
144
|
+
to: str,
|
|
145
|
+
from_email: str,
|
|
146
|
+
name: str,
|
|
147
|
+
reset_url: str,
|
|
148
|
+
product_name: str,
|
|
149
|
+
**extra_model
|
|
150
|
+
) -> Dict[str, Any]:
|
|
151
|
+
"""
|
|
152
|
+
Send password reset email.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
to: Recipient email
|
|
156
|
+
from_email: Sender email
|
|
157
|
+
name: User's name
|
|
158
|
+
reset_url: Password reset link
|
|
159
|
+
product_name: Your product/app name
|
|
160
|
+
**extra_model: Additional template variables
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Send result dict
|
|
164
|
+
"""
|
|
165
|
+
return await self.send(
|
|
166
|
+
template_alias="password-reset",
|
|
167
|
+
to=to,
|
|
168
|
+
from_email=from_email,
|
|
169
|
+
model={
|
|
170
|
+
"name": name,
|
|
171
|
+
"action_url": reset_url,
|
|
172
|
+
"product_name": product_name,
|
|
173
|
+
**extra_model
|
|
174
|
+
},
|
|
175
|
+
tag="password-reset"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
async def send_email_verification(
|
|
179
|
+
self,
|
|
180
|
+
to: str,
|
|
181
|
+
from_email: str,
|
|
182
|
+
name: str,
|
|
183
|
+
verify_url: str,
|
|
184
|
+
product_name: str,
|
|
185
|
+
**extra_model
|
|
186
|
+
) -> Dict[str, Any]:
|
|
187
|
+
"""
|
|
188
|
+
Send email verification email.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
to: Recipient email
|
|
192
|
+
from_email: Sender email
|
|
193
|
+
name: User's name
|
|
194
|
+
verify_url: Email verification link
|
|
195
|
+
product_name: Your product/app name
|
|
196
|
+
**extra_model: Additional template variables
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Send result dict
|
|
200
|
+
"""
|
|
201
|
+
return await self.send(
|
|
202
|
+
template_alias="email-verification",
|
|
203
|
+
to=to,
|
|
204
|
+
from_email=from_email,
|
|
205
|
+
model={
|
|
206
|
+
"name": name,
|
|
207
|
+
"action_url": verify_url,
|
|
208
|
+
"product_name": product_name,
|
|
209
|
+
**extra_model
|
|
210
|
+
},
|
|
211
|
+
tag="email-verification"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
async def send_invitation(
|
|
215
|
+
self,
|
|
216
|
+
to: str,
|
|
217
|
+
from_email: str,
|
|
218
|
+
name: str,
|
|
219
|
+
invite_url: str,
|
|
220
|
+
inviter_name: str,
|
|
221
|
+
product_name: str,
|
|
222
|
+
**extra_model
|
|
223
|
+
) -> Dict[str, Any]:
|
|
224
|
+
"""
|
|
225
|
+
Send user invitation email.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
to: Recipient email
|
|
229
|
+
from_email: Sender email
|
|
230
|
+
name: Invitee's name
|
|
231
|
+
invite_url: Invitation accept link
|
|
232
|
+
inviter_name: Name of person who invited
|
|
233
|
+
product_name: Your product/app name
|
|
234
|
+
**extra_model: Additional template variables
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Send result dict
|
|
238
|
+
"""
|
|
239
|
+
return await self.send(
|
|
240
|
+
template_alias="invitation",
|
|
241
|
+
to=to,
|
|
242
|
+
from_email=from_email,
|
|
243
|
+
model={
|
|
244
|
+
"name": name,
|
|
245
|
+
"action_url": invite_url,
|
|
246
|
+
"inviter_name": inviter_name,
|
|
247
|
+
"product_name": product_name,
|
|
248
|
+
**extra_model
|
|
249
|
+
},
|
|
250
|
+
tag="invitation"
|
|
251
|
+
)
|
dominus/namespaces/db.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database Namespace - Scribe data CRUD operations.
|
|
3
|
+
|
|
4
|
+
Provides data operations for all schemas with support for secure table access.
|
|
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 DbNamespace:
|
|
13
|
+
"""
|
|
14
|
+
Database CRUD namespace.
|
|
15
|
+
|
|
16
|
+
All data operations go through /api/scribe/data/* endpoints.
|
|
17
|
+
Secure tables (registered in auth.secure_tables) require a `reason` parameter.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
# List tables
|
|
21
|
+
tables = await dominus.db.tables()
|
|
22
|
+
tables = await dominus.db.tables(schema="tenant_acme")
|
|
23
|
+
|
|
24
|
+
# Query with filters
|
|
25
|
+
users = await dominus.db.query("users", filters={"status": "active"})
|
|
26
|
+
|
|
27
|
+
# Query secure table (requires reason)
|
|
28
|
+
patients = await dominus.db.query(
|
|
29
|
+
"patients",
|
|
30
|
+
schema="tenant_acme",
|
|
31
|
+
reason="Reviewing chart for appointment #123",
|
|
32
|
+
actor=current_user_id
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Insert data
|
|
36
|
+
await dominus.db.insert("users", {"name": "John", "email": "john@example.com"})
|
|
37
|
+
|
|
38
|
+
# Update rows
|
|
39
|
+
await dominus.db.update("users", {"status": "inactive"}, filters={"id": user_id})
|
|
40
|
+
|
|
41
|
+
# Delete rows
|
|
42
|
+
await dominus.db.delete("users", filters={"id": user_id})
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, client: "Dominus"):
|
|
46
|
+
self._client = client
|
|
47
|
+
|
|
48
|
+
async def tables(self, schema: str = "public") -> List[Dict[str, Any]]:
|
|
49
|
+
"""
|
|
50
|
+
List tables in a schema.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
schema: Schema name (default: "public")
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List of table metadata
|
|
57
|
+
"""
|
|
58
|
+
result = await self._client._request(
|
|
59
|
+
endpoint=f"/api/scribe/data/{schema}/tables",
|
|
60
|
+
method="GET"
|
|
61
|
+
)
|
|
62
|
+
return result.get("tables", result) if isinstance(result, dict) else result
|
|
63
|
+
|
|
64
|
+
async def columns(self, table: str, schema: str = "public") -> List[Dict[str, Any]]:
|
|
65
|
+
"""
|
|
66
|
+
List columns in a table.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
table: Table name
|
|
70
|
+
schema: Schema name (default: "public")
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
List of column metadata
|
|
74
|
+
"""
|
|
75
|
+
result = await self._client._request(
|
|
76
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/columns",
|
|
77
|
+
method="GET"
|
|
78
|
+
)
|
|
79
|
+
return result.get("columns", result) if isinstance(result, dict) else result
|
|
80
|
+
|
|
81
|
+
async def query(
|
|
82
|
+
self,
|
|
83
|
+
table: str,
|
|
84
|
+
schema: str = "public",
|
|
85
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
86
|
+
sort_by: Optional[str] = None,
|
|
87
|
+
sort_order: str = "ASC",
|
|
88
|
+
limit: int = 100,
|
|
89
|
+
offset: int = 0,
|
|
90
|
+
reason: Optional[str] = None,
|
|
91
|
+
actor: Optional[str] = None
|
|
92
|
+
) -> Dict[str, Any]:
|
|
93
|
+
"""
|
|
94
|
+
Query table data with filtering, sorting, and pagination.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
table: Table name
|
|
98
|
+
schema: Schema name (default: "public")
|
|
99
|
+
filters: Column:value filter dictionary
|
|
100
|
+
sort_by: Column to sort by
|
|
101
|
+
sort_order: "ASC" or "DESC"
|
|
102
|
+
limit: Maximum rows to return (default: 100)
|
|
103
|
+
offset: Rows to skip (default: 0)
|
|
104
|
+
reason: Access justification (required for secure tables)
|
|
105
|
+
actor: User ID or "machine" for audit trail
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Dict with "rows" and "total" keys
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
SecureTableError: If accessing secure table without reason
|
|
112
|
+
"""
|
|
113
|
+
body = {
|
|
114
|
+
"filters": filters,
|
|
115
|
+
"sort_by": sort_by,
|
|
116
|
+
"sort_order": sort_order,
|
|
117
|
+
"limit": limit,
|
|
118
|
+
"offset": offset
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if reason:
|
|
122
|
+
body["reason"] = reason
|
|
123
|
+
if actor:
|
|
124
|
+
body["actor"] = actor
|
|
125
|
+
|
|
126
|
+
return await self._client._request(
|
|
127
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/query",
|
|
128
|
+
body=body
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
async def insert(
|
|
132
|
+
self,
|
|
133
|
+
table: str,
|
|
134
|
+
data: Dict[str, Any],
|
|
135
|
+
schema: str = "public",
|
|
136
|
+
reason: Optional[str] = None,
|
|
137
|
+
actor: Optional[str] = None
|
|
138
|
+
) -> Dict[str, Any]:
|
|
139
|
+
"""
|
|
140
|
+
Insert a row into a table.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
table: Table name
|
|
144
|
+
data: Column:value dictionary
|
|
145
|
+
schema: Schema name (default: "public")
|
|
146
|
+
reason: Access justification (required for secure tables)
|
|
147
|
+
actor: User ID or "machine" for audit trail
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Inserted row data
|
|
151
|
+
"""
|
|
152
|
+
body = {"data": data}
|
|
153
|
+
|
|
154
|
+
if reason:
|
|
155
|
+
body["reason"] = reason
|
|
156
|
+
if actor:
|
|
157
|
+
body["actor"] = actor
|
|
158
|
+
|
|
159
|
+
return await self._client._request(
|
|
160
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/insert",
|
|
161
|
+
body=body
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
async def update(
|
|
165
|
+
self,
|
|
166
|
+
table: str,
|
|
167
|
+
data: Dict[str, Any],
|
|
168
|
+
filters: Dict[str, Any],
|
|
169
|
+
schema: str = "public",
|
|
170
|
+
reason: Optional[str] = None,
|
|
171
|
+
actor: Optional[str] = None
|
|
172
|
+
) -> Dict[str, Any]:
|
|
173
|
+
"""
|
|
174
|
+
Update rows matching filters.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
table: Table name
|
|
178
|
+
data: Column:value dictionary of updates
|
|
179
|
+
filters: Column:value dictionary for WHERE clause
|
|
180
|
+
schema: Schema name (default: "public")
|
|
181
|
+
reason: Access justification (required for secure tables)
|
|
182
|
+
actor: User ID or "machine" for audit trail
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Dict with "affected_rows" count
|
|
186
|
+
"""
|
|
187
|
+
body = {
|
|
188
|
+
"data": data,
|
|
189
|
+
"filters": filters
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if reason:
|
|
193
|
+
body["reason"] = reason
|
|
194
|
+
if actor:
|
|
195
|
+
body["actor"] = actor
|
|
196
|
+
|
|
197
|
+
return await self._client._request(
|
|
198
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/update",
|
|
199
|
+
body=body
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
async def delete(
|
|
203
|
+
self,
|
|
204
|
+
table: str,
|
|
205
|
+
filters: Dict[str, Any],
|
|
206
|
+
schema: str = "public",
|
|
207
|
+
reason: Optional[str] = None,
|
|
208
|
+
actor: Optional[str] = None
|
|
209
|
+
) -> Dict[str, Any]:
|
|
210
|
+
"""
|
|
211
|
+
Delete rows matching filters.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
table: Table name
|
|
215
|
+
filters: Column:value dictionary for WHERE clause
|
|
216
|
+
schema: Schema name (default: "public")
|
|
217
|
+
reason: Access justification (required for secure tables)
|
|
218
|
+
actor: User ID or "machine" for audit trail
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Dict with "affected_rows" count
|
|
222
|
+
"""
|
|
223
|
+
body = {"filters": filters}
|
|
224
|
+
|
|
225
|
+
if reason:
|
|
226
|
+
body["reason"] = reason
|
|
227
|
+
if actor:
|
|
228
|
+
body["actor"] = actor
|
|
229
|
+
|
|
230
|
+
return await self._client._request(
|
|
231
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/delete",
|
|
232
|
+
body=body
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
async def bulk_insert(
|
|
236
|
+
self,
|
|
237
|
+
table: str,
|
|
238
|
+
rows: List[Dict[str, Any]],
|
|
239
|
+
schema: str = "public",
|
|
240
|
+
reason: Optional[str] = None,
|
|
241
|
+
actor: Optional[str] = None
|
|
242
|
+
) -> Dict[str, Any]:
|
|
243
|
+
"""
|
|
244
|
+
Insert multiple rows at once.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
table: Table name
|
|
248
|
+
rows: List of column:value dictionaries
|
|
249
|
+
schema: Schema name (default: "public")
|
|
250
|
+
reason: Access justification (required for secure tables)
|
|
251
|
+
actor: User ID or "machine" for audit trail
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Dict with "inserted_count" and optionally "rows"
|
|
255
|
+
"""
|
|
256
|
+
body = {"rows": rows}
|
|
257
|
+
|
|
258
|
+
if reason:
|
|
259
|
+
body["reason"] = reason
|
|
260
|
+
if actor:
|
|
261
|
+
body["actor"] = actor
|
|
262
|
+
|
|
263
|
+
return await self._client._request(
|
|
264
|
+
endpoint=f"/api/scribe/data/{schema}/{table}/bulk-insert",
|
|
265
|
+
body=body
|
|
266
|
+
)
|
|
267
|
+
|