Skip to content

Commit 6ab37b6

Browse files
author
Вадим Козыревский
committed
Add saga id for step result
1 parent 976b3ba commit 6ab37b6

3 files changed

Lines changed: 15 additions & 5 deletions

File tree

src/cqrs/saga/saga.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import dataclasses
12
import logging
23
import types
34
import typing
@@ -278,7 +279,10 @@ async def __aiter__(
278279
if step_result is not None and executed_step is not None:
279280
# Track completed step for compensation
280281
self._completed_steps.append(executed_step)
281-
yield step_result
282+
yield dataclasses.replace(
283+
step_result,
284+
saga_id=self._saga_id,
285+
)
282286
elif executed_step is None:
283287
# Step was skipped (already completed), restore it for compensation
284288
primary_name = step_item.step.__name__
@@ -313,7 +317,10 @@ async def __aiter__(
313317
step = await self._container.resolve(step_type)
314318
self._completed_steps.append(step)
315319

316-
yield step_result
320+
yield dataclasses.replace(
321+
step_result,
322+
saga_id=self._saga_id,
323+
)
317324

318325
# Update context one final time before marking as completed
319326
await self._state_manager.update_context(self._context)

src/cqrs/saga/step.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import abc
44
import dataclasses
55
import typing
6+
import uuid
67

78
from cqrs.events.event import IEvent
89
from cqrs.response import IResponse
@@ -17,8 +18,6 @@ class SagaStepResult(typing.Generic[ContextT, Resp]):
1718
Result of a saga step execution.
1819
1920
Contains the response from the step's act method and metadata about the step.
20-
The step_type field uses typing.Any for compatibility,
21-
but the actual runtime type is Type[SagaStepHandler[ContextT, Resp]].
2221
2322
This is an internal data structure used by the saga pattern implementation.
2423
@@ -29,6 +28,8 @@ class SagaStepResult(typing.Generic[ContextT, Resp]):
2928
error_message: Error message if with_error is True
3029
error_traceback: Error traceback lines if with_error is True
3130
error_type: Type of exception if with_error is True
31+
saga_id: ID of the saga this step belongs to (set by execution layer).
32+
Enables client code to trigger compensation immediately if the saga fails.
3233
3334
Example::
3435
@@ -40,11 +41,12 @@ class SagaStepResult(typing.Generic[ContextT, Resp]):
4041
"""
4142

4243
response: Resp
43-
step_type: typing.Any # type: ignore[assignment] # Actual type: Type[SagaStepHandler[ContextT, Resp]]
44+
step_type: type[SagaStepHandler[ContextT, Resp]]
4445
with_error: bool = False
4546
error_message: str | None = None
4647
error_traceback: list[str] | None = None
4748
error_type: typing.Type[Exception] | None = None
49+
saga_id: uuid.UUID | None = None
4850

4951

5052
class SagaStepHandler(abc.ABC, typing.Generic[ContextT, Resp]):

tests/unit/test_saga/test_saga_basic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,4 @@ async def test_saga_step_result_contains_correct_metadata(
313313
assert step_result.error_message is None
314314
assert step_result.error_traceback is None
315315
assert step_result.error_type is None
316+
assert step_result.saga_id is not None

0 commit comments

Comments
 (0)