surrealdb-orm 0.1.4__py3-none-any.whl → 0.5.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.
- surreal_orm/__init__.py +72 -3
- surreal_orm/aggregations.py +164 -0
- surreal_orm/auth/__init__.py +15 -0
- surreal_orm/auth/access.py +167 -0
- surreal_orm/auth/mixins.py +302 -0
- surreal_orm/cli/__init__.py +15 -0
- surreal_orm/cli/commands.py +369 -0
- surreal_orm/connection_manager.py +58 -18
- surreal_orm/fields/__init__.py +36 -0
- surreal_orm/fields/encrypted.py +166 -0
- surreal_orm/fields/relation.py +465 -0
- surreal_orm/migrations/__init__.py +51 -0
- surreal_orm/migrations/executor.py +380 -0
- surreal_orm/migrations/generator.py +272 -0
- surreal_orm/migrations/introspector.py +305 -0
- surreal_orm/migrations/migration.py +188 -0
- surreal_orm/migrations/operations.py +531 -0
- surreal_orm/migrations/state.py +406 -0
- surreal_orm/model_base.py +530 -44
- surreal_orm/query_set.py +609 -33
- surreal_orm/relations.py +645 -0
- surreal_orm/surreal_function.py +95 -0
- surreal_orm/surreal_ql.py +113 -0
- surreal_orm/types.py +86 -0
- surreal_sdk/README.md +79 -0
- surreal_sdk/__init__.py +151 -0
- surreal_sdk/connection/__init__.py +17 -0
- surreal_sdk/connection/base.py +516 -0
- surreal_sdk/connection/http.py +421 -0
- surreal_sdk/connection/pool.py +244 -0
- surreal_sdk/connection/websocket.py +519 -0
- surreal_sdk/exceptions.py +71 -0
- surreal_sdk/functions.py +607 -0
- surreal_sdk/protocol/__init__.py +13 -0
- surreal_sdk/protocol/rpc.py +218 -0
- surreal_sdk/py.typed +0 -0
- surreal_sdk/pyproject.toml +49 -0
- surreal_sdk/streaming/__init__.py +31 -0
- surreal_sdk/streaming/change_feed.py +278 -0
- surreal_sdk/streaming/live_query.py +265 -0
- surreal_sdk/streaming/live_select.py +369 -0
- surreal_sdk/transaction.py +386 -0
- surreal_sdk/types.py +346 -0
- surrealdb_orm-0.5.0.dist-info/METADATA +465 -0
- surrealdb_orm-0.5.0.dist-info/RECORD +52 -0
- {surrealdb_orm-0.1.4.dist-info → surrealdb_orm-0.5.0.dist-info}/WHEEL +1 -1
- surrealdb_orm-0.5.0.dist-info/entry_points.txt +2 -0
- {surrealdb_orm-0.1.4.dist-info → surrealdb_orm-0.5.0.dist-info}/licenses/LICENSE +1 -1
- surrealdb_orm-0.1.4.dist-info/METADATA +0 -184
- surrealdb_orm-0.1.4.dist-info/RECORD +0 -12
surreal_sdk/types.py
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type definitions for SurrealDB SDK responses.
|
|
3
|
+
|
|
4
|
+
Provides strongly-typed wrappers around SurrealDB responses instead of raw Any types.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ResponseStatus(str, Enum):
|
|
13
|
+
"""Status of a SurrealDB response."""
|
|
14
|
+
|
|
15
|
+
OK = "OK"
|
|
16
|
+
ERR = "ERR"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class QueryResult:
|
|
21
|
+
"""
|
|
22
|
+
Result of a single query statement.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
status: OK or ERR
|
|
26
|
+
result: The query result data (records, scalar, etc.)
|
|
27
|
+
time: Execution time as reported by SurrealDB
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
status: ResponseStatus
|
|
31
|
+
result: list[dict[str, Any]] | dict[str, Any] | str | int | float | bool | None
|
|
32
|
+
time: str = ""
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_dict(cls, data: dict[str, Any]) -> "QueryResult":
|
|
36
|
+
"""Parse a query result from raw response dict."""
|
|
37
|
+
status = ResponseStatus(data.get("status", "OK"))
|
|
38
|
+
result = data.get("result")
|
|
39
|
+
time = data.get("time", "")
|
|
40
|
+
return cls(status=status, result=result, time=time)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def is_ok(self) -> bool:
|
|
44
|
+
"""Check if query succeeded."""
|
|
45
|
+
return self.status == ResponseStatus.OK
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def is_error(self) -> bool:
|
|
49
|
+
"""Check if query failed."""
|
|
50
|
+
return self.status == ResponseStatus.ERR
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def records(self) -> list[dict[str, Any]]:
|
|
54
|
+
"""Get result as list of records. Returns empty list if not applicable."""
|
|
55
|
+
if isinstance(self.result, list):
|
|
56
|
+
return self.result
|
|
57
|
+
return []
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def first(self) -> dict[str, Any] | None:
|
|
61
|
+
"""Get first record or None."""
|
|
62
|
+
records = self.records
|
|
63
|
+
return records[0] if records else None
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def scalar(self) -> str | int | float | bool | None:
|
|
67
|
+
"""Get result as scalar value."""
|
|
68
|
+
if isinstance(self.result, (str, int, float, bool)):
|
|
69
|
+
return self.result
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class QueryResponse:
|
|
75
|
+
"""
|
|
76
|
+
Response from a SurrealDB query operation.
|
|
77
|
+
|
|
78
|
+
Contains one or more QueryResult objects (one per statement in the query).
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
results: list[QueryResult] = field(default_factory=list)
|
|
82
|
+
raw: dict[str, Any] | list[Any] = field(default_factory=dict)
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def from_rpc_result(cls, data: Any) -> "QueryResponse":
|
|
86
|
+
"""Parse query response from RPC result."""
|
|
87
|
+
results: list[QueryResult] = []
|
|
88
|
+
|
|
89
|
+
if isinstance(data, list):
|
|
90
|
+
for item in data:
|
|
91
|
+
if isinstance(item, dict) and "status" in item:
|
|
92
|
+
results.append(QueryResult.from_dict(item))
|
|
93
|
+
elif isinstance(item, dict):
|
|
94
|
+
# Direct result without status wrapper
|
|
95
|
+
results.append(QueryResult(status=ResponseStatus.OK, result=item))
|
|
96
|
+
else:
|
|
97
|
+
results.append(QueryResult(status=ResponseStatus.OK, result=item))
|
|
98
|
+
elif isinstance(data, dict):
|
|
99
|
+
if "status" in data:
|
|
100
|
+
results.append(QueryResult.from_dict(data))
|
|
101
|
+
else:
|
|
102
|
+
results.append(QueryResult(status=ResponseStatus.OK, result=data))
|
|
103
|
+
|
|
104
|
+
return cls(results=results, raw=data if data else {})
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def is_ok(self) -> bool:
|
|
108
|
+
"""Check if all results succeeded."""
|
|
109
|
+
return all(r.is_ok for r in self.results)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def first_result(self) -> QueryResult | None:
|
|
113
|
+
"""Get first query result."""
|
|
114
|
+
return self.results[0] if self.results else None
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def all_records(self) -> list[dict[str, Any]]:
|
|
118
|
+
"""Get all records from all results."""
|
|
119
|
+
records: list[dict[str, Any]] = []
|
|
120
|
+
for result in self.results:
|
|
121
|
+
records.extend(result.records)
|
|
122
|
+
return records
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def is_empty(self) -> bool:
|
|
126
|
+
"""Check if response contains no records."""
|
|
127
|
+
return len(self.all_records) == 0
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def first(self) -> dict[str, Any] | None:
|
|
131
|
+
"""Get first record from all results or None."""
|
|
132
|
+
records = self.all_records
|
|
133
|
+
return records[0] if records else None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class RecordResponse:
|
|
138
|
+
"""
|
|
139
|
+
Response for single record operations (create, select one, update, etc.).
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
record: dict[str, Any] | None = None
|
|
143
|
+
raw: Any = None
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def from_rpc_result(cls, data: Any) -> "RecordResponse":
|
|
147
|
+
"""Parse record response from RPC result."""
|
|
148
|
+
record: dict[str, Any] | None = None
|
|
149
|
+
|
|
150
|
+
if isinstance(data, dict):
|
|
151
|
+
record = data
|
|
152
|
+
elif isinstance(data, list) and len(data) > 0:
|
|
153
|
+
if isinstance(data[0], dict):
|
|
154
|
+
record = data[0]
|
|
155
|
+
|
|
156
|
+
return cls(record=record, raw=data)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def exists(self) -> bool:
|
|
160
|
+
"""Check if record exists."""
|
|
161
|
+
return self.record is not None
|
|
162
|
+
|
|
163
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
164
|
+
"""Get field from record."""
|
|
165
|
+
if self.record:
|
|
166
|
+
return self.record.get(key, default)
|
|
167
|
+
return default
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def id(self) -> str | None:
|
|
171
|
+
"""Get record ID."""
|
|
172
|
+
value = self.get("id")
|
|
173
|
+
return str(value) if value is not None else None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class RecordsResponse:
|
|
178
|
+
"""
|
|
179
|
+
Response for multiple records operations (select all, etc.).
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
records: list[dict[str, Any]] = field(default_factory=list)
|
|
183
|
+
raw: Any = None
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def from_rpc_result(cls, data: Any) -> "RecordsResponse":
|
|
187
|
+
"""Parse records response from RPC result."""
|
|
188
|
+
records: list[dict[str, Any]] = []
|
|
189
|
+
|
|
190
|
+
if isinstance(data, list):
|
|
191
|
+
for item in data:
|
|
192
|
+
if isinstance(item, dict):
|
|
193
|
+
records.append(item)
|
|
194
|
+
elif isinstance(data, dict):
|
|
195
|
+
records.append(data)
|
|
196
|
+
|
|
197
|
+
return cls(records=records, raw=data)
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def count(self) -> int:
|
|
201
|
+
"""Get number of records."""
|
|
202
|
+
return len(self.records)
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def is_empty(self) -> bool:
|
|
206
|
+
"""Check if no records returned."""
|
|
207
|
+
return len(self.records) == 0
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def first(self) -> dict[str, Any] | None:
|
|
211
|
+
"""Get first record or None."""
|
|
212
|
+
return self.records[0] if self.records else None
|
|
213
|
+
|
|
214
|
+
def __iter__(self) -> Any:
|
|
215
|
+
"""Iterate over records."""
|
|
216
|
+
return iter(self.records)
|
|
217
|
+
|
|
218
|
+
def __len__(self) -> int:
|
|
219
|
+
"""Get number of records."""
|
|
220
|
+
return len(self.records)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass
|
|
224
|
+
class AuthResponse:
|
|
225
|
+
"""
|
|
226
|
+
Response from authentication operations.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
token: str | None = None
|
|
230
|
+
success: bool = False
|
|
231
|
+
raw: Any = None
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def from_rpc_result(cls, data: Any) -> "AuthResponse":
|
|
235
|
+
"""Parse auth response from RPC result."""
|
|
236
|
+
token: str | None = None
|
|
237
|
+
success = False
|
|
238
|
+
|
|
239
|
+
if isinstance(data, str):
|
|
240
|
+
token = data
|
|
241
|
+
success = True
|
|
242
|
+
elif data is None:
|
|
243
|
+
# signin/signup with no token return = success
|
|
244
|
+
success = True
|
|
245
|
+
|
|
246
|
+
return cls(token=token, success=success, raw=data)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass
|
|
250
|
+
class InfoResponse:
|
|
251
|
+
"""
|
|
252
|
+
Response from INFO operations.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
256
|
+
raw: Any = None
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def from_rpc_result(cls, data: Any) -> "InfoResponse":
|
|
260
|
+
"""Parse info response from RPC result."""
|
|
261
|
+
info_data: dict[str, Any] = {}
|
|
262
|
+
|
|
263
|
+
if isinstance(data, dict):
|
|
264
|
+
info_data = data
|
|
265
|
+
elif isinstance(data, list) and len(data) > 0:
|
|
266
|
+
if isinstance(data[0], dict):
|
|
267
|
+
info_data = data[0]
|
|
268
|
+
|
|
269
|
+
return cls(data=info_data, raw=data)
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def tables(self) -> dict[str, Any]:
|
|
273
|
+
"""Get tables info."""
|
|
274
|
+
result = self.data.get("tables", self.data.get("tb", {}))
|
|
275
|
+
return result if isinstance(result, dict) else {}
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def namespaces(self) -> dict[str, Any]:
|
|
279
|
+
"""Get namespaces info."""
|
|
280
|
+
result = self.data.get("namespaces", self.data.get("ns", {}))
|
|
281
|
+
return result if isinstance(result, dict) else {}
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def databases(self) -> dict[str, Any]:
|
|
285
|
+
"""Get databases info."""
|
|
286
|
+
result = self.data.get("databases", self.data.get("db", {}))
|
|
287
|
+
return result if isinstance(result, dict) else {}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@dataclass
|
|
291
|
+
class LiveQueryId:
|
|
292
|
+
"""
|
|
293
|
+
Wrapper for Live Query UUID.
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
uuid: str
|
|
297
|
+
|
|
298
|
+
def __str__(self) -> str:
|
|
299
|
+
return self.uuid
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
def from_rpc_result(cls, data: Any) -> "LiveQueryId":
|
|
303
|
+
"""Parse live query ID from RPC result."""
|
|
304
|
+
if isinstance(data, str):
|
|
305
|
+
return cls(uuid=data)
|
|
306
|
+
elif isinstance(data, list) and len(data) > 0:
|
|
307
|
+
first = data[0]
|
|
308
|
+
if isinstance(first, str):
|
|
309
|
+
return cls(uuid=first)
|
|
310
|
+
elif isinstance(first, dict) and "result" in first:
|
|
311
|
+
return cls(uuid=str(first["result"]))
|
|
312
|
+
raise ValueError(f"Cannot parse live query ID from: {data}")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@dataclass
|
|
316
|
+
class DeleteResponse:
|
|
317
|
+
"""
|
|
318
|
+
Response from delete operations.
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
deleted: list[dict[str, Any]] = field(default_factory=list)
|
|
322
|
+
raw: Any = None
|
|
323
|
+
|
|
324
|
+
@classmethod
|
|
325
|
+
def from_rpc_result(cls, data: Any) -> "DeleteResponse":
|
|
326
|
+
"""Parse delete response from RPC result."""
|
|
327
|
+
deleted: list[dict[str, Any]] = []
|
|
328
|
+
|
|
329
|
+
if isinstance(data, list):
|
|
330
|
+
for item in data:
|
|
331
|
+
if isinstance(item, dict):
|
|
332
|
+
deleted.append(item)
|
|
333
|
+
elif isinstance(data, dict):
|
|
334
|
+
deleted.append(data)
|
|
335
|
+
|
|
336
|
+
return cls(deleted=deleted, raw=data)
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def count(self) -> int:
|
|
340
|
+
"""Number of deleted records."""
|
|
341
|
+
return len(self.deleted)
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def success(self) -> bool:
|
|
345
|
+
"""Check if any records were deleted."""
|
|
346
|
+
return len(self.deleted) > 0
|