kurrentdbclient 0.3__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.
- kurrentdbclient/__init__.py +49 -0
- kurrentdbclient/asyncio_client.py +1662 -0
- kurrentdbclient/client.py +1914 -0
- kurrentdbclient/common.py +535 -0
- kurrentdbclient/connection.py +107 -0
- kurrentdbclient/connection_spec.py +371 -0
- kurrentdbclient/events.py +141 -0
- kurrentdbclient/exceptions.py +239 -0
- kurrentdbclient/gossip.py +104 -0
- kurrentdbclient/instrumentation/__init__.py +0 -0
- kurrentdbclient/instrumentation/opentelemetry/__init__.py +185 -0
- kurrentdbclient/instrumentation/opentelemetry/attributes.py +20 -0
- kurrentdbclient/instrumentation/opentelemetry/grpc.py +165 -0
- kurrentdbclient/instrumentation/opentelemetry/package.py +2 -0
- kurrentdbclient/instrumentation/opentelemetry/spanners.py +1097 -0
- kurrentdbclient/instrumentation/opentelemetry/utils.py +199 -0
- kurrentdbclient/instrumentation/opentelemetry/version.py +2 -0
- kurrentdbclient/persistent.py +1982 -0
- kurrentdbclient/projections.py +735 -0
- kurrentdbclient/protos/Grpc/cluster_pb2.py +92 -0
- kurrentdbclient/protos/Grpc/cluster_pb2.pyi +765 -0
- kurrentdbclient/protos/Grpc/cluster_pb2_grpc.py +514 -0
- kurrentdbclient/protos/Grpc/code_pb2.py +37 -0
- kurrentdbclient/protos/Grpc/code_pb2.pyi +357 -0
- kurrentdbclient/protos/Grpc/code_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/gossip_pb2.py +46 -0
- kurrentdbclient/protos/Grpc/gossip_pb2.pyi +126 -0
- kurrentdbclient/protos/Grpc/gossip_pb2_grpc.py +98 -0
- kurrentdbclient/protos/Grpc/persistent_pb2.py +140 -0
- kurrentdbclient/protos/Grpc/persistent_pb2.pyi +1135 -0
- kurrentdbclient/protos/Grpc/persistent_pb2_grpc.py +399 -0
- kurrentdbclient/protos/Grpc/projections_pb2.py +99 -0
- kurrentdbclient/protos/Grpc/projections_pb2.pyi +558 -0
- kurrentdbclient/protos/Grpc/projections_pb2_grpc.py +485 -0
- kurrentdbclient/protos/Grpc/shared_pb2.py +62 -0
- kurrentdbclient/protos/Grpc/shared_pb2.pyi +218 -0
- kurrentdbclient/protos/Grpc/shared_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/status_pb2.py +39 -0
- kurrentdbclient/protos/Grpc/status_pb2.pyi +67 -0
- kurrentdbclient/protos/Grpc/status_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/streams_pb2.py +132 -0
- kurrentdbclient/protos/Grpc/streams_pb2.pyi +1038 -0
- kurrentdbclient/protos/Grpc/streams_pb2_grpc.py +269 -0
- kurrentdbclient/py.typed +0 -0
- kurrentdbclient/streams.py +1400 -0
- kurrentdbclient-0.3.dist-info/LICENSE +29 -0
- kurrentdbclient-0.3.dist-info/METADATA +3769 -0
- kurrentdbclient-0.3.dist-info/RECORD +49 -0
- kurrentdbclient-0.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from typing import Any, Dict, Optional, Sequence
|
|
3
|
+
from urllib.parse import ParseResult, parse_qs, urlparse
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
URI_SCHEME_ESDB = "esdb"
|
|
7
|
+
URI_SCHEME_ESDB_DISCOVER = "esdb+discover"
|
|
8
|
+
URI_SCHEME_KURRENTDB = "kurrentdb"
|
|
9
|
+
URI_SCHEME_KURRENTDB_DISCOVER = "kurrentdb+discover"
|
|
10
|
+
URI_SCHEME_KDB = "kdb"
|
|
11
|
+
URI_SCHEME_KDB_DISCOVER = "kdb+discover"
|
|
12
|
+
|
|
13
|
+
URI_SCHEMES_NON_DISCOVER = [
|
|
14
|
+
URI_SCHEME_ESDB,
|
|
15
|
+
URI_SCHEME_KURRENTDB,
|
|
16
|
+
URI_SCHEME_KDB,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
URI_SCHEMES_DISCOVER = [
|
|
20
|
+
URI_SCHEME_ESDB_DISCOVER,
|
|
21
|
+
URI_SCHEME_KURRENTDB_DISCOVER,
|
|
22
|
+
URI_SCHEME_KDB_DISCOVER,
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
URI_SCHEMES_ALL = URI_SCHEMES_NON_DISCOVER + URI_SCHEMES_DISCOVER
|
|
26
|
+
|
|
27
|
+
NODE_PREFERENCE_LEADER = "leader"
|
|
28
|
+
NODE_PREFERENCE_FOLLOWER = "follower"
|
|
29
|
+
NODE_PREFERENCE_RANDOM = "random"
|
|
30
|
+
NODE_PREFERENCE_REPLICA = "readonlyreplica"
|
|
31
|
+
VALID_NODE_PREFERENCES = [
|
|
32
|
+
NODE_PREFERENCE_LEADER,
|
|
33
|
+
NODE_PREFERENCE_FOLLOWER,
|
|
34
|
+
NODE_PREFERENCE_RANDOM,
|
|
35
|
+
NODE_PREFERENCE_REPLICA,
|
|
36
|
+
]
|
|
37
|
+
VALID_CONNECTION_QUERY_STRING_FIELDS = [
|
|
38
|
+
"Tls",
|
|
39
|
+
"ConnectionName",
|
|
40
|
+
"MaxDiscoverAttempts",
|
|
41
|
+
"DiscoveryInterval",
|
|
42
|
+
"GossipTimeout",
|
|
43
|
+
"NodePreference",
|
|
44
|
+
"TlsVerifyCert",
|
|
45
|
+
"DefaultDeadline",
|
|
46
|
+
"KeepAliveInterval",
|
|
47
|
+
"KeepAliveTimeout",
|
|
48
|
+
"TlsCaFile",
|
|
49
|
+
"UserCertFile",
|
|
50
|
+
"UserKeyFile",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ConnectionOptions:
|
|
55
|
+
__slots__ = [f"_{s}" for s in VALID_CONNECTION_QUERY_STRING_FIELDS]
|
|
56
|
+
|
|
57
|
+
def __init__(self, query: str):
|
|
58
|
+
# Parse query string (case insensitivity, assume single values).
|
|
59
|
+
options = {k.upper(): v[0] for k, v in parse_qs(query).items()}
|
|
60
|
+
|
|
61
|
+
self._validate_field_names(options)
|
|
62
|
+
self._set_Tls(options)
|
|
63
|
+
self._set_ConnectionName(options)
|
|
64
|
+
self._set_MaxDiscoverAttempts(options)
|
|
65
|
+
self._set_DiscoveryInterval(options)
|
|
66
|
+
self._set_GossipTimeout(options)
|
|
67
|
+
self._set_NodePreference(options)
|
|
68
|
+
self._set_TlsVerifyCert(options)
|
|
69
|
+
self._set_DefaultDeadline(options)
|
|
70
|
+
self._set_KeepAliveInterval(options)
|
|
71
|
+
self._set_KeepAliveTimeout(options)
|
|
72
|
+
self._set_TlsCaFile(options)
|
|
73
|
+
self._set_UserCertFile(options)
|
|
74
|
+
self._set_UserKeyFile(options)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _validate_field_names(options: Dict[str, Any]) -> None:
|
|
78
|
+
valid_fields = [s.upper() for s in VALID_CONNECTION_QUERY_STRING_FIELDS]
|
|
79
|
+
invalid_fields = []
|
|
80
|
+
for field in options.keys():
|
|
81
|
+
if field not in valid_fields:
|
|
82
|
+
invalid_fields.append(field)
|
|
83
|
+
if len(invalid_fields) > 0:
|
|
84
|
+
plural = "s" if len(invalid_fields) > 1 else ""
|
|
85
|
+
joined_fields = ", ".join(invalid_fields)
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"Unknown field{plural} in connection query string: {joined_fields}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def _set_Tls(self, options: Dict[str, Any]) -> None:
|
|
91
|
+
_Tls = options.get("Tls".upper())
|
|
92
|
+
if _Tls is None:
|
|
93
|
+
self._Tls = True
|
|
94
|
+
else:
|
|
95
|
+
validTlsValues = ["true", "false"]
|
|
96
|
+
if _Tls.lower() not in validTlsValues:
|
|
97
|
+
raise ValueError(f"'{_Tls}' not one of: {', '.join(validTlsValues)}")
|
|
98
|
+
elif _Tls.lower() == "true":
|
|
99
|
+
self._Tls = True
|
|
100
|
+
else:
|
|
101
|
+
self._Tls = False
|
|
102
|
+
|
|
103
|
+
def _set_ConnectionName(self, options: Dict[str, Any]) -> None:
|
|
104
|
+
_ConnectionName = options.get("ConnectionName".upper())
|
|
105
|
+
if _ConnectionName is None:
|
|
106
|
+
self._ConnectionName = str(uuid4())
|
|
107
|
+
else:
|
|
108
|
+
self._ConnectionName = _ConnectionName
|
|
109
|
+
|
|
110
|
+
def _set_MaxDiscoverAttempts(self, options: Dict[str, Any]) -> None:
|
|
111
|
+
_MaxDiscoverAttempts = options.get("MaxDiscoverAttempts".upper())
|
|
112
|
+
if _MaxDiscoverAttempts is None:
|
|
113
|
+
self._MaxDiscoverAttempts = 10
|
|
114
|
+
else:
|
|
115
|
+
self._MaxDiscoverAttempts = int(_MaxDiscoverAttempts)
|
|
116
|
+
|
|
117
|
+
def _set_DiscoveryInterval(self, options: Dict[str, Any]) -> None:
|
|
118
|
+
_DiscoveryInterval = options.get("DiscoveryInterval".upper())
|
|
119
|
+
if _DiscoveryInterval is None:
|
|
120
|
+
self._DiscoveryInterval = 100
|
|
121
|
+
else:
|
|
122
|
+
self._DiscoveryInterval = int(_DiscoveryInterval)
|
|
123
|
+
|
|
124
|
+
def _set_GossipTimeout(self, options: Dict[str, Any]) -> None:
|
|
125
|
+
_GossipTimeout = options.get("GossipTimeout".upper())
|
|
126
|
+
if _GossipTimeout is None:
|
|
127
|
+
self._GossipTimeout = 5
|
|
128
|
+
else:
|
|
129
|
+
self._GossipTimeout = int(_GossipTimeout)
|
|
130
|
+
|
|
131
|
+
def _set_NodePreference(self, options: Dict[str, Any]) -> None:
|
|
132
|
+
_NodePreference = options.get("NodePreference".upper())
|
|
133
|
+
if _NodePreference is None:
|
|
134
|
+
self._NodePreference = NODE_PREFERENCE_LEADER
|
|
135
|
+
else:
|
|
136
|
+
if _NodePreference.lower() not in VALID_NODE_PREFERENCES:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f"'{_NodePreference}' not one of:"
|
|
139
|
+
f" {', '.join(VALID_NODE_PREFERENCES)}"
|
|
140
|
+
)
|
|
141
|
+
self._NodePreference = _NodePreference.lower()
|
|
142
|
+
|
|
143
|
+
def _set_TlsVerifyCert(self, options: Dict[str, Any]) -> None:
|
|
144
|
+
_TlsVerifyCert = options.get("TlsVerifyCert".upper())
|
|
145
|
+
if _TlsVerifyCert is None:
|
|
146
|
+
self._TlsVerifyCert = True
|
|
147
|
+
else:
|
|
148
|
+
validTlsVerifyCertValues = ["true", "false"]
|
|
149
|
+
if _TlsVerifyCert.lower() not in validTlsVerifyCertValues:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"'{_TlsVerifyCert}' not one of:"
|
|
152
|
+
f" {', '.join(validTlsVerifyCertValues)}"
|
|
153
|
+
)
|
|
154
|
+
elif _TlsVerifyCert.lower() == "true":
|
|
155
|
+
self._TlsVerifyCert = True
|
|
156
|
+
else:
|
|
157
|
+
self._TlsVerifyCert = False
|
|
158
|
+
|
|
159
|
+
def _set_DefaultDeadline(self, options: Dict[str, Any]) -> None:
|
|
160
|
+
_DefaultDeadline = options.get("DefaultDeadline".upper())
|
|
161
|
+
if _DefaultDeadline is None:
|
|
162
|
+
self._DefaultDeadline: Optional[int] = None
|
|
163
|
+
else:
|
|
164
|
+
self._DefaultDeadline = int(_DefaultDeadline)
|
|
165
|
+
|
|
166
|
+
def _set_KeepAliveInterval(self, options: Dict[str, Any]) -> None:
|
|
167
|
+
_KeepAliveInterval = options.get("KeepAliveInterval".upper())
|
|
168
|
+
if _KeepAliveInterval is None:
|
|
169
|
+
self._KeepAliveInterval: Optional[int] = None
|
|
170
|
+
else:
|
|
171
|
+
self._KeepAliveInterval = int(_KeepAliveInterval)
|
|
172
|
+
|
|
173
|
+
def _set_KeepAliveTimeout(self, options: Dict[str, Any]) -> None:
|
|
174
|
+
_KeepAliveTimeout = options.get("KeepAliveTimeout".upper())
|
|
175
|
+
if _KeepAliveTimeout is None:
|
|
176
|
+
self._KeepAliveTimeout: Optional[int] = None
|
|
177
|
+
else:
|
|
178
|
+
self._KeepAliveTimeout = int(_KeepAliveTimeout)
|
|
179
|
+
|
|
180
|
+
def _set_TlsCaFile(self, options: Dict[str, Any]) -> None:
|
|
181
|
+
_TlsCaFile = options.get("TlsCaFile".upper())
|
|
182
|
+
if _TlsCaFile is None:
|
|
183
|
+
self._TlsCaFile: Optional[str] = None
|
|
184
|
+
else:
|
|
185
|
+
self._TlsCaFile = str(_TlsCaFile)
|
|
186
|
+
|
|
187
|
+
def _set_UserCertFile(self, options: Dict[str, Any]) -> None:
|
|
188
|
+
_UserCertFile = options.get("UserCertFile".upper())
|
|
189
|
+
if _UserCertFile is None:
|
|
190
|
+
self._UserCertFile: Optional[str] = None
|
|
191
|
+
else:
|
|
192
|
+
self._UserCertFile = str(_UserCertFile)
|
|
193
|
+
|
|
194
|
+
def _set_UserKeyFile(self, options: Dict[str, Any]) -> None:
|
|
195
|
+
_UserKeyFile = options.get("UserKeyFile".upper())
|
|
196
|
+
if _UserKeyFile is None:
|
|
197
|
+
self._UserKeyFile: Optional[str] = None
|
|
198
|
+
else:
|
|
199
|
+
self._UserKeyFile = str(_UserKeyFile)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def Tls(self) -> bool:
|
|
203
|
+
"""
|
|
204
|
+
Controls whether client will use a secure channel (has to match server).
|
|
205
|
+
|
|
206
|
+
Valid values in URI: 'true', 'false'.
|
|
207
|
+
"""
|
|
208
|
+
return self._Tls
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def ConnectionName(self) -> str:
|
|
212
|
+
"""
|
|
213
|
+
This value is sent as header 'connection-name' in all calls to server.
|
|
214
|
+
|
|
215
|
+
Defaults to a new version 4 UUID string.
|
|
216
|
+
"""
|
|
217
|
+
return self._ConnectionName
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def MaxDiscoverAttempts(self) -> int:
|
|
221
|
+
"""
|
|
222
|
+
Number of attempts to connect to gossip before giving up.
|
|
223
|
+
"""
|
|
224
|
+
return self._MaxDiscoverAttempts
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def DiscoveryInterval(self) -> int:
|
|
228
|
+
"""
|
|
229
|
+
How long to wait (in milliseconds) between gossip retries.
|
|
230
|
+
"""
|
|
231
|
+
return self._DiscoveryInterval
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def GossipTimeout(self) -> int:
|
|
235
|
+
"""
|
|
236
|
+
How long to wait (in seconds) for a response to a request to gossip API.
|
|
237
|
+
"""
|
|
238
|
+
return self._GossipTimeout
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def NodePreference(
|
|
242
|
+
self,
|
|
243
|
+
) -> str:
|
|
244
|
+
"""
|
|
245
|
+
Controls whether requests are directed to another node.
|
|
246
|
+
|
|
247
|
+
Value values: 'leader', 'follower', 'random', 'readonlyreplica'.
|
|
248
|
+
"""
|
|
249
|
+
return self._NodePreference
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def TlsVerifyCert(self) -> bool:
|
|
253
|
+
"""
|
|
254
|
+
Controls whether certificate is verified.
|
|
255
|
+
|
|
256
|
+
Valid values in URI: 'true', 'false'.
|
|
257
|
+
"""
|
|
258
|
+
return self._TlsVerifyCert
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def DefaultDeadline(self) -> Optional[int]:
|
|
262
|
+
"""
|
|
263
|
+
Default deadline (in seconds) for calls to the server that write data.
|
|
264
|
+
"""
|
|
265
|
+
return self._DefaultDeadline
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def KeepAliveInterval(self) -> Optional[int]:
|
|
269
|
+
"""
|
|
270
|
+
gRPC "keep alive" interval (in milliseconds).
|
|
271
|
+
"""
|
|
272
|
+
return self._KeepAliveInterval
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def KeepAliveTimeout(self) -> Optional[int]:
|
|
276
|
+
"""
|
|
277
|
+
gRPC "keep alive timeout" (in milliseconds).
|
|
278
|
+
"""
|
|
279
|
+
return self._KeepAliveTimeout
|
|
280
|
+
|
|
281
|
+
@property
|
|
282
|
+
def TlsCaFile(self) -> Optional[str]:
|
|
283
|
+
"""
|
|
284
|
+
Path to file containing root CA certificate(s) to verify server.
|
|
285
|
+
"""
|
|
286
|
+
return self._TlsCaFile
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def UserCertFile(self) -> Optional[str]:
|
|
290
|
+
"""
|
|
291
|
+
Path to file containing user X.509 certificate.
|
|
292
|
+
"""
|
|
293
|
+
return self._UserCertFile
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def UserKeyFile(self) -> Optional[str]:
|
|
297
|
+
"""
|
|
298
|
+
Path to file containing user X.509 key.
|
|
299
|
+
"""
|
|
300
|
+
return self._UserKeyFile
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class ConnectionSpec:
|
|
304
|
+
__slots__ = [
|
|
305
|
+
"_uri",
|
|
306
|
+
"_scheme",
|
|
307
|
+
"_netloc",
|
|
308
|
+
"_username",
|
|
309
|
+
"_password",
|
|
310
|
+
"_targets",
|
|
311
|
+
"_options",
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
def __init__(self, uri: Optional[str] = None):
|
|
315
|
+
self._uri = uri or ""
|
|
316
|
+
parse_result: ParseResult = urlparse(self._uri)
|
|
317
|
+
if parse_result.scheme not in URI_SCHEMES_ALL:
|
|
318
|
+
raise ValueError(
|
|
319
|
+
f"Invalid URI scheme: '{parse_result.scheme}' not in:"
|
|
320
|
+
f" {', '.join(URI_SCHEMES_ALL)}: {uri}"
|
|
321
|
+
)
|
|
322
|
+
self._scheme = parse_result.scheme
|
|
323
|
+
self._netloc = parse_result.netloc
|
|
324
|
+
self._username = parse_result.username
|
|
325
|
+
self._password = parse_result.password
|
|
326
|
+
if "@" in self._netloc:
|
|
327
|
+
_, _, targets = self._netloc.partition("@")
|
|
328
|
+
else:
|
|
329
|
+
targets = self._netloc
|
|
330
|
+
self._targets = [t.strip() for t in targets.split(",") if t.strip()]
|
|
331
|
+
|
|
332
|
+
if len(self._targets) == 0:
|
|
333
|
+
raise ValueError(f"No targets specified: {uri}")
|
|
334
|
+
if self._scheme in URI_SCHEMES_DISCOVER:
|
|
335
|
+
if len(self._targets) > 1:
|
|
336
|
+
raise ValueError(f"More than one target specified: {uri}")
|
|
337
|
+
|
|
338
|
+
for i, target in enumerate(self._targets):
|
|
339
|
+
host, _, port = target.partition(":")
|
|
340
|
+
if port == "":
|
|
341
|
+
port = "2113"
|
|
342
|
+
self._targets[i] = f"{host}:{port}"
|
|
343
|
+
|
|
344
|
+
self._options = ConnectionOptions(parse_result.query)
|
|
345
|
+
if self._options.Tls is True:
|
|
346
|
+
if not self._username or not self._password:
|
|
347
|
+
raise ValueError(f"Username and password are required: {uri}")
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def uri(self) -> str:
|
|
351
|
+
return self._uri
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def scheme(self) -> str:
|
|
355
|
+
return self._scheme
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def username(self) -> Optional[str]:
|
|
359
|
+
return self._username
|
|
360
|
+
|
|
361
|
+
@property
|
|
362
|
+
def password(self) -> Optional[str]:
|
|
363
|
+
return self._password
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def targets(self) -> Sequence[str]:
|
|
367
|
+
return self._targets
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def options(self) -> ConnectionOptions:
|
|
371
|
+
return self._options
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from typing_extensions import Literal
|
|
8
|
+
|
|
9
|
+
ContentType = Literal["application/json", "application/octet-stream"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class NewEvent:
|
|
14
|
+
"""
|
|
15
|
+
Encapsulates event data to be recorded in KurrentDB.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
type: str
|
|
19
|
+
data: bytes
|
|
20
|
+
metadata: bytes = b""
|
|
21
|
+
content_type: ContentType = "application/json"
|
|
22
|
+
id: UUID = field(default_factory=uuid4)
|
|
23
|
+
|
|
24
|
+
def __eq__(self, other: object) -> bool:
|
|
25
|
+
return isinstance(other, (NewEvent, RecordedEvent)) and self.id == other.id
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class RecordedEvent:
|
|
30
|
+
"""
|
|
31
|
+
Encapsulates event data that has been recorded in KurrentDB.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
type: str
|
|
35
|
+
data: bytes
|
|
36
|
+
metadata: bytes
|
|
37
|
+
content_type: str
|
|
38
|
+
id: UUID
|
|
39
|
+
stream_name: str
|
|
40
|
+
stream_position: int
|
|
41
|
+
commit_position: Optional[int]
|
|
42
|
+
prepare_position: Optional[int]
|
|
43
|
+
recorded_at: Optional[datetime] = None
|
|
44
|
+
link: Optional["RecordedEvent"] = None
|
|
45
|
+
retry_count: Optional[int] = None
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def ack_id(self) -> UUID:
|
|
49
|
+
if self.link is not None:
|
|
50
|
+
return self.link.id
|
|
51
|
+
else:
|
|
52
|
+
return self.id
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def is_system_event(self) -> bool:
|
|
56
|
+
return self.type.startswith("$")
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def is_link_event(self) -> bool:
|
|
60
|
+
return self.type == "$>"
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_resolved_event(self) -> bool:
|
|
64
|
+
return self.link is not None
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def is_checkpoint(self) -> bool:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def is_caught_up(self) -> bool:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
# @property
|
|
75
|
+
# def is_fell_behind(self) -> bool:
|
|
76
|
+
# return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True)
|
|
80
|
+
class Checkpoint(RecordedEvent):
|
|
81
|
+
CHECKPOINT_ID = UUID("00000000-0000-0000-0000-000000000000")
|
|
82
|
+
|
|
83
|
+
def __init__(self, commit_position: int, prepare_position: int) -> None:
|
|
84
|
+
super().__init__(
|
|
85
|
+
id=Checkpoint.CHECKPOINT_ID,
|
|
86
|
+
type="",
|
|
87
|
+
data=b"",
|
|
88
|
+
content_type="",
|
|
89
|
+
metadata=b"",
|
|
90
|
+
stream_name="",
|
|
91
|
+
stream_position=0,
|
|
92
|
+
commit_position=commit_position,
|
|
93
|
+
prepare_position=prepare_position,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def is_checkpoint(self) -> bool:
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True)
|
|
102
|
+
class CaughtUp(RecordedEvent):
|
|
103
|
+
CAUGHT_UP_ID = UUID("00000000-0000-0000-0000-000000000000")
|
|
104
|
+
|
|
105
|
+
def __init__(self) -> None:
|
|
106
|
+
super().__init__(
|
|
107
|
+
id=CaughtUp.CAUGHT_UP_ID,
|
|
108
|
+
type="",
|
|
109
|
+
data=b"",
|
|
110
|
+
content_type="",
|
|
111
|
+
metadata=b"",
|
|
112
|
+
stream_name="",
|
|
113
|
+
stream_position=0,
|
|
114
|
+
commit_position=0,
|
|
115
|
+
prepare_position=0,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def is_caught_up(self) -> bool:
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# @dataclass(frozen=True)
|
|
124
|
+
# class FellBehind(RecordedEvent):
|
|
125
|
+
# FELL_BEHIND_ID = UUID("00000000-0000-0000-0000-000000000000")
|
|
126
|
+
#
|
|
127
|
+
# def __init__(self) -> None:
|
|
128
|
+
# super().__init__(
|
|
129
|
+
# id=FellBehind.FELL_BEHIND_ID,
|
|
130
|
+
# type="",
|
|
131
|
+
# data=b"",
|
|
132
|
+
# content_type="",
|
|
133
|
+
# metadata=b"",
|
|
134
|
+
# stream_name="",
|
|
135
|
+
# stream_position=0,
|
|
136
|
+
# commit_position=0,
|
|
137
|
+
# )
|
|
138
|
+
#
|
|
139
|
+
# @property
|
|
140
|
+
# def is_fell_behind(self) -> bool:
|
|
141
|
+
# return True
|