hexkit 3.1.0__tar.gz → 3.2.1__tar.gz
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.
- {hexkit-3.1.0/src/hexkit.egg-info → hexkit-3.2.1}/PKG-INFO +1 -1
- {hexkit-3.1.0 → hexkit-3.2.1}/pyproject.toml +1 -1
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/correlation.py +15 -1
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/daopub.py +3 -2
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongokafka/provider.py +43 -18
- {hexkit-3.1.0 → hexkit-3.2.1/src/hexkit.egg-info}/PKG-INFO +1 -1
- {hexkit-3.1.0 → hexkit-3.2.1}/LICENSE +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/MANIFEST.in +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/README.md +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/setup.cfg +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/__main__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/base.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/config.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/custom_types.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/log.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/dao.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/daosub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/eventpub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/eventsub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/protocols/objstorage.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/config.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/provider/daosub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/provider/eventpub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/provider/eventsub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/provider/utils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/testcontainer.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/akafka/testutils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongodb/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongodb/provider.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongodb/testutils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongokafka/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/mongokafka/testutils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/provider.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/s3/testutils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/testing/__init__.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/providers/testing/eventpub.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/py.typed +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit/utils.py +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit.egg-info/SOURCES.txt +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit.egg-info/dependency_links.txt +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit.egg-info/requires.txt +0 -0
- {hexkit-3.1.0 → hexkit-3.2.1}/src/hexkit.egg-info/top_level.txt +0 -0
|
@@ -29,6 +29,7 @@ correlation_id_var: ContextVar[str] = ContextVar("correlation_id", default="")
|
|
|
29
29
|
|
|
30
30
|
__all__ = [
|
|
31
31
|
"set_correlation_id",
|
|
32
|
+
"set_new_correlation_id",
|
|
32
33
|
"get_correlation_id",
|
|
33
34
|
"new_correlation_id",
|
|
34
35
|
"validate_correlation_id",
|
|
@@ -40,6 +41,9 @@ __all__ = [
|
|
|
40
41
|
class CorrelationIdContextError(RuntimeError):
|
|
41
42
|
"""Raised when the correlation ID ContextVar is unexpectedly not set."""
|
|
42
43
|
|
|
44
|
+
def __init__(self):
|
|
45
|
+
super().__init__("No correlation ID has been set.")
|
|
46
|
+
|
|
43
47
|
|
|
44
48
|
class InvalidCorrelationIdError(RuntimeError):
|
|
45
49
|
"""Raised when a correlation ID fails validation."""
|
|
@@ -68,7 +72,7 @@ def validate_correlation_id(correlation_id: str):
|
|
|
68
72
|
|
|
69
73
|
@asynccontextmanager
|
|
70
74
|
async def set_correlation_id(correlation_id: str):
|
|
71
|
-
"""Set the correlation ID for the life of the context.
|
|
75
|
+
"""Set the given correlation ID for the life of the context.
|
|
72
76
|
|
|
73
77
|
Raises:
|
|
74
78
|
InvalidCorrelationIdError: when the correlation ID is empty or invalid.
|
|
@@ -80,6 +84,16 @@ async def set_correlation_id(correlation_id: str):
|
|
|
80
84
|
yield
|
|
81
85
|
|
|
82
86
|
|
|
87
|
+
@asynccontextmanager
|
|
88
|
+
async def set_new_correlation_id():
|
|
89
|
+
"""Set a new correlation ID for the life of the context."""
|
|
90
|
+
correlation_id = new_correlation_id()
|
|
91
|
+
|
|
92
|
+
async with set_context_var(correlation_id_var, correlation_id):
|
|
93
|
+
log.info("Set context correlation ID to %s", correlation_id)
|
|
94
|
+
yield correlation_id
|
|
95
|
+
|
|
96
|
+
|
|
83
97
|
def get_correlation_id() -> str:
|
|
84
98
|
"""Get the correlation ID.
|
|
85
99
|
|
|
@@ -62,7 +62,7 @@ class DaoPublisherFactoryProtocol(DaoFactoryBase, ABC):
|
|
|
62
62
|
dto_model: type[Dto],
|
|
63
63
|
id_field: str,
|
|
64
64
|
fields_to_index: Optional[Collection[str]] = None,
|
|
65
|
-
dto_to_event: Callable[[Dto], JsonObject],
|
|
65
|
+
dto_to_event: Callable[[Dto], Optional[JsonObject]],
|
|
66
66
|
event_topic: str,
|
|
67
67
|
autopublish: bool = True,
|
|
68
68
|
) -> DaoPublisher[Dto]:
|
|
@@ -82,6 +82,7 @@ class DaoPublisherFactoryProtocol(DaoFactoryBase, ABC):
|
|
|
82
82
|
`id_field`. Defaults to None.
|
|
83
83
|
dto_to_event:
|
|
84
84
|
A function that takes a DTO and returns the payload for an event.
|
|
85
|
+
If the returned payload is None, the event will not be published.
|
|
85
86
|
event_topic:
|
|
86
87
|
The topic to which events should be published.
|
|
87
88
|
autopublish:
|
|
@@ -120,7 +121,7 @@ class DaoPublisherFactoryProtocol(DaoFactoryBase, ABC):
|
|
|
120
121
|
dto_model: type[Dto],
|
|
121
122
|
id_field: str,
|
|
122
123
|
fields_to_index: Optional[Collection[str]],
|
|
123
|
-
dto_to_event: Callable[[Dto], JsonObject],
|
|
124
|
+
dto_to_event: Callable[[Dto], Optional[JsonObject]],
|
|
124
125
|
event_topic: str,
|
|
125
126
|
autopublish: bool,
|
|
126
127
|
) -> DaoPublisher[Dto]:
|
|
@@ -28,6 +28,7 @@ from aiokafka import AIOKafkaProducer
|
|
|
28
28
|
from motor.core import AgnosticCollection
|
|
29
29
|
from motor.motor_asyncio import AsyncIOMotorClient
|
|
30
30
|
|
|
31
|
+
from hexkit.correlation import get_correlation_id, set_correlation_id
|
|
31
32
|
from hexkit.custom_types import JsonObject
|
|
32
33
|
from hexkit.protocols.dao import (
|
|
33
34
|
DaoNaturalId,
|
|
@@ -86,7 +87,13 @@ def dto_to_document(
|
|
|
86
87
|
"""
|
|
87
88
|
document = json.loads(dto.model_dump_json())
|
|
88
89
|
document["_id"] = document.pop(id_field)
|
|
89
|
-
|
|
90
|
+
|
|
91
|
+
correlation_id = get_correlation_id()
|
|
92
|
+
document["__metadata__"] = {
|
|
93
|
+
"deleted": False,
|
|
94
|
+
"published": published,
|
|
95
|
+
"correlation_id": correlation_id,
|
|
96
|
+
}
|
|
90
97
|
|
|
91
98
|
return document
|
|
92
99
|
|
|
@@ -94,7 +101,7 @@ def dto_to_document(
|
|
|
94
101
|
def get_change_publish_func(
|
|
95
102
|
id_field: str,
|
|
96
103
|
event_topic: str,
|
|
97
|
-
dto_to_event: Callable[[Dto], JsonObject],
|
|
104
|
+
dto_to_event: Callable[[Dto], Optional[JsonObject]],
|
|
98
105
|
event_publisher: EventPublisherProtocol,
|
|
99
106
|
collection: AgnosticCollection,
|
|
100
107
|
) -> Callable[[Dto], Awaitable[None]]:
|
|
@@ -103,12 +110,13 @@ def get_change_publish_func(
|
|
|
103
110
|
async def publish_change(dto: Dto) -> None:
|
|
104
111
|
"""Publishes a change event and marks the change as published."""
|
|
105
112
|
payload = dto_to_event(dto)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
if payload is not None:
|
|
114
|
+
await event_publisher.publish(
|
|
115
|
+
payload=payload,
|
|
116
|
+
type_=CHANGE_EVENT_TYPE,
|
|
117
|
+
key=getattr(dto, id_field),
|
|
118
|
+
topic=event_topic,
|
|
119
|
+
)
|
|
112
120
|
|
|
113
121
|
document = dto_to_document(dto, id_field=id_field, published=True)
|
|
114
122
|
await collection.replace_one({"_id": document["_id"]}, document, upsert=True)
|
|
@@ -134,7 +142,15 @@ def get_delete_publish_func(
|
|
|
134
142
|
topic=event_topic,
|
|
135
143
|
)
|
|
136
144
|
|
|
137
|
-
|
|
145
|
+
correlation_id = get_correlation_id() # Get active correlation first
|
|
146
|
+
document = {
|
|
147
|
+
"_id": id_,
|
|
148
|
+
"__metadata__": {
|
|
149
|
+
"deleted": True,
|
|
150
|
+
"published": True,
|
|
151
|
+
"correlation_id": correlation_id,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
138
154
|
await collection.replace_one({"_id": document["_id"]}, document)
|
|
139
155
|
|
|
140
156
|
return publish_deletion
|
|
@@ -233,7 +249,9 @@ class MongoKafkaDaoPublisher(Generic[Dto]):
|
|
|
233
249
|
ResourceNotFoundError:
|
|
234
250
|
when resource with the id specified in the dto was not found
|
|
235
251
|
"""
|
|
252
|
+
correlation_id = get_correlation_id()
|
|
236
253
|
document = self._dao._dto_to_document(dto)
|
|
254
|
+
document["__metadata__.correlation_id"] = correlation_id
|
|
237
255
|
result = await self._collection.replace_one(
|
|
238
256
|
{"_id": document["_id"], "__metadata__.deleted": False}, document
|
|
239
257
|
)
|
|
@@ -252,9 +270,14 @@ class MongoKafkaDaoPublisher(Generic[Dto]):
|
|
|
252
270
|
Raises:
|
|
253
271
|
ResourceNotFoundError: when resource with the specified id_ was not found
|
|
254
272
|
"""
|
|
273
|
+
correlation_id = get_correlation_id()
|
|
255
274
|
document = {
|
|
256
275
|
"_id": id_,
|
|
257
|
-
"__metadata__": {
|
|
276
|
+
"__metadata__": {
|
|
277
|
+
"deleted": True,
|
|
278
|
+
"published": False,
|
|
279
|
+
"correlation_id": correlation_id,
|
|
280
|
+
},
|
|
258
281
|
}
|
|
259
282
|
result = await self._collection.replace_one(
|
|
260
283
|
{"_id": document["_id"], "__metadata__.deleted": False}, document
|
|
@@ -343,13 +366,15 @@ class MongoKafkaDaoPublisher(Generic[Dto]):
|
|
|
343
366
|
|
|
344
367
|
async def publish_document(self, document: dict[str, Any]) -> None:
|
|
345
368
|
"""Publishes a document"""
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
369
|
+
correlation_id = document.get("__metadata__", {}).get("correlation_id", "")
|
|
370
|
+
async with set_correlation_id(correlation_id=correlation_id):
|
|
371
|
+
if document.get("__metadata__", {}).get("deleted", False):
|
|
372
|
+
await self._publish_delete(document["_id"])
|
|
373
|
+
else:
|
|
374
|
+
dto = document_to_dto(
|
|
375
|
+
document, id_field=self._id_field, dto_model=self._dto_model
|
|
376
|
+
)
|
|
377
|
+
await self._publish_change(dto)
|
|
353
378
|
|
|
354
379
|
async def publish_pending(self) -> None:
|
|
355
380
|
"""Publishes all non-published changes."""
|
|
@@ -422,7 +447,7 @@ class MongoKafkaDaoPublisherFactory(DaoPublisherFactoryProtocol):
|
|
|
422
447
|
dto_model: type[Dto],
|
|
423
448
|
id_field: str,
|
|
424
449
|
fields_to_index: Optional[Collection[str]],
|
|
425
|
-
dto_to_event: Callable[[Dto], JsonObject],
|
|
450
|
+
dto_to_event: Callable[[Dto], Optional[JsonObject]],
|
|
426
451
|
event_topic: str,
|
|
427
452
|
autopublish: bool,
|
|
428
453
|
) -> DaoPublisher[Dto]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|