eventsourcing 9.4.0b2__tar.gz → 9.4.0b4__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.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/PKG-INFO +1 -1
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/application.py +53 -104
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/cipher.py +3 -8
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/compressor.py +2 -6
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/cryptography.py +3 -8
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/dispatch.py +2 -2
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/domain.py +361 -384
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/interface.py +10 -24
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/persistence.py +91 -212
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/popo.py +2 -2
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/postgres.py +7 -10
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/projection.py +19 -41
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/sqlite.py +4 -7
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/system.py +89 -156
- eventsourcing-9.4.0b4/eventsourcing/tests/__init__.py +3 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/tests/application.py +14 -10
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/tests/domain.py +14 -34
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/tests/persistence.py +21 -18
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/utils.py +11 -17
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/pyproject.toml +30 -36
- eventsourcing-9.4.0b2/eventsourcing/tests/__init__.py +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/AUTHORS +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/LICENSE +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/README.md +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/__init__.py +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/py.typed +0 -0
- {eventsourcing-9.4.0b2 → eventsourcing-9.4.0b4}/eventsourcing/tests/postgres_utils.py +0 -0
|
@@ -72,8 +72,7 @@ def project_aggregate(
|
|
|
72
72
|
aggregate: TMutableOrImmutableAggregate | None,
|
|
73
73
|
domain_events: Iterable[DomainEventProtocol],
|
|
74
74
|
) -> TMutableOrImmutableAggregate | None:
|
|
75
|
-
"""
|
|
76
|
-
Projector function for aggregate projections, which works
|
|
75
|
+
"""Projector function for aggregate projections, which works
|
|
77
76
|
by successively calling aggregate mutator function mutate()
|
|
78
77
|
on each of the given list of domain events in turn.
|
|
79
78
|
"""
|
|
@@ -102,8 +101,7 @@ class Cache(Generic[S, T]):
|
|
|
102
101
|
|
|
103
102
|
|
|
104
103
|
class LRUCache(Cache[S, T]):
|
|
105
|
-
"""
|
|
106
|
-
Size limited caching that tracks accesses by recency.
|
|
104
|
+
"""Size limited caching that tracks accesses by recency.
|
|
107
105
|
|
|
108
106
|
This is basically copied from functools.lru_cache. But
|
|
109
107
|
we need to know when there was a cache hit, so we can
|
|
@@ -205,7 +203,8 @@ class Repository:
|
|
|
205
203
|
"""Reconstructs aggregates from events in an
|
|
206
204
|
:class:`~eventsourcing.persistence.EventStore`,
|
|
207
205
|
possibly using snapshot store to avoid replaying
|
|
208
|
-
all events.
|
|
206
|
+
all events.
|
|
207
|
+
"""
|
|
209
208
|
|
|
210
209
|
FASTFORWARD_LOCKS_CACHE_MAXSIZE = 50
|
|
211
210
|
|
|
@@ -219,8 +218,7 @@ class Repository:
|
|
|
219
218
|
fastforward_skipping: bool = False,
|
|
220
219
|
deepcopy_from_cache: bool = True,
|
|
221
220
|
):
|
|
222
|
-
"""
|
|
223
|
-
Initialises repository with given event store (an
|
|
221
|
+
"""Initialises repository with given event store (an
|
|
224
222
|
:class:`~eventsourcing.persistence.EventStore` for aggregate
|
|
225
223
|
:class:`~eventsourcing.domain.AggregateEvent` objects)
|
|
226
224
|
and optionally a snapshot store (an
|
|
@@ -258,15 +256,14 @@ class Repository:
|
|
|
258
256
|
fastforward_skipping: bool = False,
|
|
259
257
|
deepcopy_from_cache: bool = True,
|
|
260
258
|
) -> TMutableOrImmutableAggregate:
|
|
261
|
-
"""
|
|
262
|
-
Reconstructs an :class:`~eventsourcing.domain.Aggregate` for a
|
|
259
|
+
"""Reconstructs an :class:`~eventsourcing.domain.Aggregate` for a
|
|
263
260
|
given ID from stored events, optionally at a particular version.
|
|
264
261
|
"""
|
|
265
262
|
if self.cache and version is None:
|
|
266
263
|
try:
|
|
267
264
|
# Look for aggregate in the cache.
|
|
268
265
|
aggregate = cast(
|
|
269
|
-
TMutableOrImmutableAggregate, self.cache.get(aggregate_id)
|
|
266
|
+
"TMutableOrImmutableAggregate", self.cache.get(aggregate_id)
|
|
270
267
|
)
|
|
271
268
|
except KeyError:
|
|
272
269
|
# Reconstruct aggregate from stored events.
|
|
@@ -287,7 +284,11 @@ class Repository:
|
|
|
287
284
|
originator_id=aggregate_id, gt=aggregate.version
|
|
288
285
|
)
|
|
289
286
|
_aggregate = projector_func(
|
|
290
|
-
aggregate,
|
|
287
|
+
aggregate,
|
|
288
|
+
cast(
|
|
289
|
+
"Iterable[TDomainEvent]",
|
|
290
|
+
new_events,
|
|
291
|
+
),
|
|
291
292
|
)
|
|
292
293
|
if _aggregate is None:
|
|
293
294
|
raise AggregateNotFoundError(aggregate_id)
|
|
@@ -342,8 +343,8 @@ class Repository:
|
|
|
342
343
|
aggregate = projector_func(
|
|
343
344
|
initial,
|
|
344
345
|
chain(
|
|
345
|
-
cast(Iterable[TDomainEvent], snapshots),
|
|
346
|
-
cast(Iterable[TDomainEvent], aggregate_events),
|
|
346
|
+
cast("Iterable[TDomainEvent]", snapshots),
|
|
347
|
+
cast("Iterable[TDomainEvent]", aggregate_events),
|
|
347
348
|
),
|
|
348
349
|
)
|
|
349
350
|
|
|
@@ -379,9 +380,7 @@ class Repository:
|
|
|
379
380
|
self._fastforward_locks_inuse[aggregate_id] = (lock_, num_users)
|
|
380
381
|
|
|
381
382
|
def __contains__(self, item: UUID) -> bool:
|
|
382
|
-
"""
|
|
383
|
-
Tests to see if an aggregate exists in the repository.
|
|
384
|
-
"""
|
|
383
|
+
"""Tests to see if an aggregate exists in the repository."""
|
|
385
384
|
try:
|
|
386
385
|
self.get(aggregate_id=item)
|
|
387
386
|
except AggregateNotFoundError:
|
|
@@ -392,8 +391,7 @@ class Repository:
|
|
|
392
391
|
|
|
393
392
|
@dataclass(frozen=True)
|
|
394
393
|
class Section:
|
|
395
|
-
"""
|
|
396
|
-
Frozen dataclass that represents a section from a :class:`NotificationLog`.
|
|
394
|
+
"""Frozen dataclass that represents a section from a :class:`NotificationLog`.
|
|
397
395
|
The :data:`items` attribute contains a list of
|
|
398
396
|
:class:`~eventsourcing.persistence.Notification` objects.
|
|
399
397
|
The :data:`id` attribute is the section ID, two integers
|
|
@@ -416,14 +414,11 @@ class Section:
|
|
|
416
414
|
|
|
417
415
|
|
|
418
416
|
class NotificationLog(ABC):
|
|
419
|
-
"""
|
|
420
|
-
Abstract base class for notification logs.
|
|
421
|
-
"""
|
|
417
|
+
"""Abstract base class for notification logs."""
|
|
422
418
|
|
|
423
419
|
@abstractmethod
|
|
424
420
|
def __getitem__(self, section_id: str) -> Section:
|
|
425
|
-
"""
|
|
426
|
-
Returns a :class:`Section` of
|
|
421
|
+
"""Returns a :class:`Section` of
|
|
427
422
|
:class:`~eventsourcing.persistence.Notification` objects
|
|
428
423
|
from the notification log.
|
|
429
424
|
"""
|
|
@@ -438,16 +433,14 @@ class NotificationLog(ABC):
|
|
|
438
433
|
*,
|
|
439
434
|
inclusive_of_start: bool = True,
|
|
440
435
|
) -> list[Notification]:
|
|
441
|
-
"""
|
|
442
|
-
Returns a selection of
|
|
436
|
+
"""Returns a selection of
|
|
443
437
|
:class:`~eventsourcing.persistence.Notification` objects
|
|
444
438
|
from the notification log.
|
|
445
439
|
"""
|
|
446
440
|
|
|
447
441
|
|
|
448
442
|
class LocalNotificationLog(NotificationLog):
|
|
449
|
-
"""
|
|
450
|
-
Notification log that presents sections of event notifications
|
|
443
|
+
"""Notification log that presents sections of event notifications
|
|
451
444
|
retrieved from an :class:`~eventsourcing.persistence.ApplicationRecorder`.
|
|
452
445
|
"""
|
|
453
446
|
|
|
@@ -458,8 +451,7 @@ class LocalNotificationLog(NotificationLog):
|
|
|
458
451
|
recorder: ApplicationRecorder,
|
|
459
452
|
section_size: int = DEFAULT_SECTION_SIZE,
|
|
460
453
|
):
|
|
461
|
-
"""
|
|
462
|
-
Initialises a local notification object with given
|
|
454
|
+
"""Initialises a local notification object with given
|
|
463
455
|
:class:`~eventsourcing.persistence.ApplicationRecorder`
|
|
464
456
|
and an optional section size.
|
|
465
457
|
|
|
@@ -474,8 +466,7 @@ class LocalNotificationLog(NotificationLog):
|
|
|
474
466
|
self.section_size = section_size
|
|
475
467
|
|
|
476
468
|
def __getitem__(self, requested_section_id: str) -> Section:
|
|
477
|
-
"""
|
|
478
|
-
Returns a :class:`Section` of event notifications
|
|
469
|
+
"""Returns a :class:`Section` of event notifications
|
|
479
470
|
based on the requested section ID. The section ID of
|
|
480
471
|
the returned section will describe the event
|
|
481
472
|
notifications that are actually contained in
|
|
@@ -528,8 +519,7 @@ class LocalNotificationLog(NotificationLog):
|
|
|
528
519
|
*,
|
|
529
520
|
inclusive_of_start: bool = True,
|
|
530
521
|
) -> list[Notification]:
|
|
531
|
-
"""
|
|
532
|
-
Returns a selection of
|
|
522
|
+
"""Returns a selection of
|
|
533
523
|
:class:`~eventsourcing.persistence.Notification` objects
|
|
534
524
|
from the notification log.
|
|
535
525
|
"""
|
|
@@ -552,17 +542,14 @@ class LocalNotificationLog(NotificationLog):
|
|
|
552
542
|
|
|
553
543
|
|
|
554
544
|
class ProcessingEvent:
|
|
555
|
-
"""
|
|
556
|
-
Keeps together a :class:`~eventsourcing.persistence.Tracking`
|
|
545
|
+
"""Keeps together a :class:`~eventsourcing.persistence.Tracking`
|
|
557
546
|
object, which represents the position of a domain event notification
|
|
558
547
|
in the notification log of a particular application, and the
|
|
559
548
|
new domain events that result from processing that notification.
|
|
560
549
|
"""
|
|
561
550
|
|
|
562
551
|
def __init__(self, tracking: Tracking | None = None):
|
|
563
|
-
"""
|
|
564
|
-
Initialises the process event with the given tracking object.
|
|
565
|
-
"""
|
|
552
|
+
"""Initialises the process event with the given tracking object."""
|
|
566
553
|
self.tracking = tracking
|
|
567
554
|
self.events: list[DomainEventProtocol] = []
|
|
568
555
|
self.aggregates: dict[UUID, MutableOrImmutableAggregate] = {}
|
|
@@ -573,9 +560,7 @@ class ProcessingEvent:
|
|
|
573
560
|
*objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
|
|
574
561
|
**kwargs: Any,
|
|
575
562
|
) -> None:
|
|
576
|
-
"""
|
|
577
|
-
Collects pending domain events from the given aggregate.
|
|
578
|
-
"""
|
|
563
|
+
"""Collects pending domain events from the given aggregate."""
|
|
579
564
|
for obj in objs:
|
|
580
565
|
if obj is None:
|
|
581
566
|
continue
|
|
@@ -604,9 +589,7 @@ class ProcessingEvent:
|
|
|
604
589
|
|
|
605
590
|
|
|
606
591
|
class Application:
|
|
607
|
-
"""
|
|
608
|
-
Base class for event-sourced applications.
|
|
609
|
-
"""
|
|
592
|
+
"""Base class for event-sourced applications."""
|
|
610
593
|
|
|
611
594
|
name = "Application"
|
|
612
595
|
env: ClassVar[dict[str, str]] = {}
|
|
@@ -629,8 +612,7 @@ class Application:
|
|
|
629
612
|
cls.name = cls.__name__
|
|
630
613
|
|
|
631
614
|
def __init__(self, env: EnvType | None = None) -> None:
|
|
632
|
-
"""
|
|
633
|
-
Initialises an application with an
|
|
615
|
+
"""Initialises an application with an
|
|
634
616
|
:class:`~eventsourcing.persistence.InfrastructureFactory`,
|
|
635
617
|
a :class:`~eventsourcing.persistence.Mapper`,
|
|
636
618
|
an :class:`~eventsourcing.persistence.ApplicationRecorder`,
|
|
@@ -652,15 +634,12 @@ class Application:
|
|
|
652
634
|
|
|
653
635
|
@property
|
|
654
636
|
def repository(self) -> Repository:
|
|
655
|
-
"""
|
|
656
|
-
An application's repository reconstructs aggregates from stored events.
|
|
657
|
-
"""
|
|
637
|
+
"""An application's repository reconstructs aggregates from stored events."""
|
|
658
638
|
return self._repository
|
|
659
639
|
|
|
660
640
|
@property
|
|
661
641
|
def notification_log(self) -> LocalNotificationLog:
|
|
662
|
-
"""
|
|
663
|
-
An application's notification log presents all the aggregate events
|
|
642
|
+
"""An application's notification log presents all the aggregate events
|
|
664
643
|
of an application in the order they were recorded as a sequence of event
|
|
665
644
|
notifications.
|
|
666
645
|
"""
|
|
@@ -676,9 +655,7 @@ class Application:
|
|
|
676
655
|
return self._notification_log
|
|
677
656
|
|
|
678
657
|
def construct_env(self, name: str, env: EnvType | None = None) -> Environment:
|
|
679
|
-
"""
|
|
680
|
-
Constructs environment from which application will be configured.
|
|
681
|
-
"""
|
|
658
|
+
"""Constructs environment from which application will be configured."""
|
|
682
659
|
_env = dict(type(self).env)
|
|
683
660
|
if type(self).is_snapshotting_enabled or type(self).snapshotting_intervals:
|
|
684
661
|
_env["IS_SNAPSHOTTING_ENABLED"] = "y"
|
|
@@ -688,22 +665,19 @@ class Application:
|
|
|
688
665
|
return Environment(name, _env)
|
|
689
666
|
|
|
690
667
|
def construct_factory(self, env: Environment) -> InfrastructureFactory:
|
|
691
|
-
"""
|
|
692
|
-
Constructs an :class:`~eventsourcing.persistence.InfrastructureFactory`
|
|
668
|
+
"""Constructs an :class:`~eventsourcing.persistence.InfrastructureFactory`
|
|
693
669
|
for use by the application.
|
|
694
670
|
"""
|
|
695
671
|
return InfrastructureFactory.construct(env)
|
|
696
672
|
|
|
697
673
|
def construct_mapper(self) -> Mapper:
|
|
698
|
-
"""
|
|
699
|
-
Constructs a :class:`~eventsourcing.persistence.Mapper`
|
|
674
|
+
"""Constructs a :class:`~eventsourcing.persistence.Mapper`
|
|
700
675
|
for use by the application.
|
|
701
676
|
"""
|
|
702
677
|
return self.factory.mapper(transcoder=self.construct_transcoder())
|
|
703
678
|
|
|
704
679
|
def construct_transcoder(self) -> Transcoder:
|
|
705
|
-
"""
|
|
706
|
-
Constructs a :class:`~eventsourcing.persistence.Transcoder`
|
|
680
|
+
"""Constructs a :class:`~eventsourcing.persistence.Transcoder`
|
|
707
681
|
for use by the application.
|
|
708
682
|
"""
|
|
709
683
|
transcoder = self.factory.transcoder()
|
|
@@ -712,8 +686,7 @@ class Application:
|
|
|
712
686
|
return transcoder
|
|
713
687
|
|
|
714
688
|
def register_transcodings(self, transcoder: JSONTranscoder) -> None:
|
|
715
|
-
"""
|
|
716
|
-
Registers :class:`~eventsourcing.persistence.Transcoding`
|
|
689
|
+
"""Registers :class:`~eventsourcing.persistence.Transcoding`
|
|
717
690
|
objects on given :class:`~eventsourcing.persistence.JSONTranscoder`.
|
|
718
691
|
"""
|
|
719
692
|
transcoder.register(UUIDAsHex())
|
|
@@ -721,15 +694,13 @@ class Application:
|
|
|
721
694
|
transcoder.register(DatetimeAsISO())
|
|
722
695
|
|
|
723
696
|
def construct_recorder(self) -> ApplicationRecorder:
|
|
724
|
-
"""
|
|
725
|
-
Constructs an :class:`~eventsourcing.persistence.ApplicationRecorder`
|
|
697
|
+
"""Constructs an :class:`~eventsourcing.persistence.ApplicationRecorder`
|
|
726
698
|
for use by the application.
|
|
727
699
|
"""
|
|
728
700
|
return self.factory.application_recorder()
|
|
729
701
|
|
|
730
702
|
def construct_event_store(self) -> EventStore:
|
|
731
|
-
"""
|
|
732
|
-
Constructs an :class:`~eventsourcing.persistence.EventStore`
|
|
703
|
+
"""Constructs an :class:`~eventsourcing.persistence.EventStore`
|
|
733
704
|
for use by the application to store and retrieve aggregate
|
|
734
705
|
:class:`~eventsourcing.domain.AggregateEvent` objects.
|
|
735
706
|
"""
|
|
@@ -739,8 +710,7 @@ class Application:
|
|
|
739
710
|
)
|
|
740
711
|
|
|
741
712
|
def construct_snapshot_store(self) -> EventStore:
|
|
742
|
-
"""
|
|
743
|
-
Constructs an :class:`~eventsourcing.persistence.EventStore`
|
|
713
|
+
"""Constructs an :class:`~eventsourcing.persistence.EventStore`
|
|
744
714
|
for use by the application to store and retrieve aggregate
|
|
745
715
|
:class:`~eventsourcing.domain.Snapshot` objects.
|
|
746
716
|
"""
|
|
@@ -751,9 +721,7 @@ class Application:
|
|
|
751
721
|
)
|
|
752
722
|
|
|
753
723
|
def construct_repository(self) -> Repository:
|
|
754
|
-
"""
|
|
755
|
-
Constructs a :class:`Repository` for use by the application.
|
|
756
|
-
"""
|
|
724
|
+
"""Constructs a :class:`Repository` for use by the application."""
|
|
757
725
|
cache_maxsize_envvar = self.env.get(self.AGGREGATE_CACHE_MAXSIZE)
|
|
758
726
|
cache_maxsize = int(cache_maxsize_envvar) if cache_maxsize_envvar else None
|
|
759
727
|
return Repository(
|
|
@@ -770,9 +738,7 @@ class Application:
|
|
|
770
738
|
)
|
|
771
739
|
|
|
772
740
|
def construct_notification_log(self) -> LocalNotificationLog:
|
|
773
|
-
"""
|
|
774
|
-
Constructs a :class:`LocalNotificationLog` for use by the application.
|
|
775
|
-
"""
|
|
741
|
+
"""Constructs a :class:`LocalNotificationLog` for use by the application."""
|
|
776
742
|
return LocalNotificationLog(self.recorder, section_size=self.log_section_size)
|
|
777
743
|
|
|
778
744
|
def save(
|
|
@@ -780,8 +746,7 @@ class Application:
|
|
|
780
746
|
*objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
|
|
781
747
|
**kwargs: Any,
|
|
782
748
|
) -> list[Recording]:
|
|
783
|
-
"""
|
|
784
|
-
Collects pending events from given aggregates and
|
|
749
|
+
"""Collects pending events from given aggregates and
|
|
785
750
|
puts them in the application's event store.
|
|
786
751
|
"""
|
|
787
752
|
processing_event = ProcessingEvent()
|
|
@@ -793,9 +758,7 @@ class Application:
|
|
|
793
758
|
return recordings
|
|
794
759
|
|
|
795
760
|
def _record(self, processing_event: ProcessingEvent) -> list[Recording]:
|
|
796
|
-
"""
|
|
797
|
-
Records given process event in the application's recorder.
|
|
798
|
-
"""
|
|
761
|
+
"""Records given process event in the application's recorder."""
|
|
799
762
|
recordings = self.events.put(
|
|
800
763
|
processing_event.events,
|
|
801
764
|
tracking=processing_event.tracking,
|
|
@@ -849,8 +812,7 @@ class Application:
|
|
|
849
812
|
TMutableOrImmutableAggregate, TDomainEvent
|
|
850
813
|
] = project_aggregate,
|
|
851
814
|
) -> None:
|
|
852
|
-
"""
|
|
853
|
-
Takes a snapshot of the recorded state of the aggregate,
|
|
815
|
+
"""Takes a snapshot of the recorded state of the aggregate,
|
|
854
816
|
and puts the snapshot in the snapshot store.
|
|
855
817
|
"""
|
|
856
818
|
if self.snapshots is None:
|
|
@@ -870,8 +832,7 @@ class Application:
|
|
|
870
832
|
self.snapshots.put([snapshot])
|
|
871
833
|
|
|
872
834
|
def notify(self, new_events: list[DomainEventProtocol]) -> None:
|
|
873
|
-
"""
|
|
874
|
-
Deprecated.
|
|
835
|
+
"""Deprecated.
|
|
875
836
|
|
|
876
837
|
Called after new aggregate events have been saved. This
|
|
877
838
|
method on this class doesn't actually do anything,
|
|
@@ -880,8 +841,7 @@ class Application:
|
|
|
880
841
|
"""
|
|
881
842
|
|
|
882
843
|
def _notify(self, recordings: list[Recording]) -> None:
|
|
883
|
-
"""
|
|
884
|
-
Called after new aggregate events have been saved. This
|
|
844
|
+
"""Called after new aggregate events have been saved. This
|
|
885
845
|
method on this class doesn't actually do anything,
|
|
886
846
|
but this method may be implemented by subclasses that
|
|
887
847
|
need to take action when new domain events have been saved.
|
|
@@ -896,15 +856,13 @@ TApplication = TypeVar("TApplication", bound=Application)
|
|
|
896
856
|
|
|
897
857
|
|
|
898
858
|
class AggregateNotFoundError(EventSourcingError):
|
|
899
|
-
"""
|
|
900
|
-
Raised when an :class:`~eventsourcing.domain.Aggregate`
|
|
859
|
+
"""Raised when an :class:`~eventsourcing.domain.Aggregate`
|
|
901
860
|
object is not found in a :class:`Repository`.
|
|
902
861
|
"""
|
|
903
862
|
|
|
904
863
|
|
|
905
864
|
class EventSourcedLog(Generic[TDomainEvent]):
|
|
906
|
-
"""
|
|
907
|
-
Constructs a sequence of domain events, like an aggregate.
|
|
865
|
+
"""Constructs a sequence of domain events, like an aggregate.
|
|
908
866
|
But unlike an aggregate the events can be triggered
|
|
909
867
|
and selected for use in an application without
|
|
910
868
|
reconstructing a current state from all the events.
|
|
@@ -932,9 +890,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
932
890
|
next_originator_version: int | None = None,
|
|
933
891
|
**kwargs: Any,
|
|
934
892
|
) -> TDomainEvent:
|
|
935
|
-
"""
|
|
936
|
-
Constructs and returns a new log event.
|
|
937
|
-
"""
|
|
893
|
+
"""Constructs and returns a new log event."""
|
|
938
894
|
return self._trigger_event(
|
|
939
895
|
logged_cls=self.logged_cls,
|
|
940
896
|
next_originator_version=next_originator_version,
|
|
@@ -947,9 +903,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
947
903
|
next_originator_version: int | None = None,
|
|
948
904
|
**kwargs: Any,
|
|
949
905
|
) -> SDomainEvent:
|
|
950
|
-
"""
|
|
951
|
-
Constructs and returns a new log event.
|
|
952
|
-
"""
|
|
906
|
+
"""Constructs and returns a new log event."""
|
|
953
907
|
if next_originator_version is None:
|
|
954
908
|
last_logged = self.get_last()
|
|
955
909
|
if last_logged is None:
|
|
@@ -965,18 +919,14 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
965
919
|
)
|
|
966
920
|
|
|
967
921
|
def get_first(self) -> TDomainEvent | None:
|
|
968
|
-
"""
|
|
969
|
-
Selects the first logged event.
|
|
970
|
-
"""
|
|
922
|
+
"""Selects the first logged event."""
|
|
971
923
|
try:
|
|
972
924
|
return next(self.get(limit=1))
|
|
973
925
|
except StopIteration:
|
|
974
926
|
return None
|
|
975
927
|
|
|
976
928
|
def get_last(self) -> TDomainEvent | None:
|
|
977
|
-
"""
|
|
978
|
-
Selects the last logged event.
|
|
979
|
-
"""
|
|
929
|
+
"""Selects the last logged event."""
|
|
980
930
|
try:
|
|
981
931
|
return next(self.get(desc=True, limit=1))
|
|
982
932
|
except StopIteration:
|
|
@@ -990,12 +940,11 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
990
940
|
desc: bool = False,
|
|
991
941
|
limit: int | None = None,
|
|
992
942
|
) -> Iterator[TDomainEvent]:
|
|
993
|
-
"""
|
|
994
|
-
Selects a range of logged events with limit,
|
|
943
|
+
"""Selects a range of logged events with limit,
|
|
995
944
|
with ascending or descending order.
|
|
996
945
|
"""
|
|
997
946
|
return cast(
|
|
998
|
-
Iterator[TDomainEvent],
|
|
947
|
+
"Iterator[TDomainEvent]",
|
|
999
948
|
self.events.get(
|
|
1000
949
|
originator_id=self.originator_id,
|
|
1001
950
|
gt=gt,
|
|
@@ -17,8 +17,7 @@ if TYPE_CHECKING:
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class AESCipher(Cipher):
|
|
20
|
-
"""
|
|
21
|
-
Cipher strategy that uses AES cipher (in GCM mode)
|
|
20
|
+
"""Cipher strategy that uses AES cipher (in GCM mode)
|
|
22
21
|
from the Python pycryptodome package.
|
|
23
22
|
"""
|
|
24
23
|
|
|
@@ -27,8 +26,7 @@ class AESCipher(Cipher):
|
|
|
27
26
|
|
|
28
27
|
@staticmethod
|
|
29
28
|
def create_key(num_bytes: int) -> str:
|
|
30
|
-
"""
|
|
31
|
-
Creates AES cipher key, with length num_bytes.
|
|
29
|
+
"""Creates AES cipher key, with length num_bytes.
|
|
32
30
|
|
|
33
31
|
:param num_bytes: An int value, either 16, 24, or 32.
|
|
34
32
|
|
|
@@ -47,8 +45,7 @@ class AESCipher(Cipher):
|
|
|
47
45
|
return os.urandom(num_bytes)
|
|
48
46
|
|
|
49
47
|
def __init__(self, environment: Environment):
|
|
50
|
-
"""
|
|
51
|
-
Initialises AES cipher with ``cipher_key``.
|
|
48
|
+
"""Initialises AES cipher with ``cipher_key``.
|
|
52
49
|
|
|
53
50
|
:param str cipher_key: 16, 24, or 32 bytes encoded as base64
|
|
54
51
|
"""
|
|
@@ -62,7 +59,6 @@ class AESCipher(Cipher):
|
|
|
62
59
|
|
|
63
60
|
def encrypt(self, plaintext: bytes) -> bytes:
|
|
64
61
|
"""Return ciphertext for given plaintext."""
|
|
65
|
-
|
|
66
62
|
# Construct AES-GCM cipher, with 96-bit nonce.
|
|
67
63
|
nonce = AESCipher.random_bytes(12)
|
|
68
64
|
cipher = self.construct_cipher(nonce)
|
|
@@ -87,7 +83,6 @@ class AESCipher(Cipher):
|
|
|
87
83
|
|
|
88
84
|
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
89
85
|
"""Return plaintext for given ciphertext."""
|
|
90
|
-
|
|
91
86
|
# Split out the nonce, tag, and encrypted data.
|
|
92
87
|
nonce = ciphertext[:12]
|
|
93
88
|
if len(nonce) != 12:
|
|
@@ -7,13 +7,9 @@ from eventsourcing.persistence import Compressor
|
|
|
7
7
|
|
|
8
8
|
class ZlibCompressor(Compressor):
|
|
9
9
|
def compress(self, data: bytes) -> bytes:
|
|
10
|
-
"""
|
|
11
|
-
Compress bytes using zlib.
|
|
12
|
-
"""
|
|
10
|
+
"""Compress bytes using zlib."""
|
|
13
11
|
return zlib.compress(data)
|
|
14
12
|
|
|
15
13
|
def decompress(self, data: bytes) -> bytes:
|
|
16
|
-
"""
|
|
17
|
-
Decompress bytes using zlib.
|
|
18
|
-
"""
|
|
14
|
+
"""Decompress bytes using zlib."""
|
|
19
15
|
return zlib.decompress(data)
|
|
@@ -14,8 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class AESCipher(Cipher):
|
|
17
|
-
"""
|
|
18
|
-
Cipher strategy that uses AES cipher (in GCM mode)
|
|
17
|
+
"""Cipher strategy that uses AES cipher (in GCM mode)
|
|
19
18
|
from the Python cryptography package.
|
|
20
19
|
"""
|
|
21
20
|
|
|
@@ -24,8 +23,7 @@ class AESCipher(Cipher):
|
|
|
24
23
|
|
|
25
24
|
@staticmethod
|
|
26
25
|
def create_key(num_bytes: int) -> str:
|
|
27
|
-
"""
|
|
28
|
-
Creates AES cipher key, with length num_bytes.
|
|
26
|
+
"""Creates AES cipher key, with length num_bytes.
|
|
29
27
|
|
|
30
28
|
:param num_bytes: An int value, either 16, 24, or 32.
|
|
31
29
|
|
|
@@ -45,8 +43,7 @@ class AESCipher(Cipher):
|
|
|
45
43
|
return os.urandom(num_bytes)
|
|
46
44
|
|
|
47
45
|
def __init__(self, environment: Environment):
|
|
48
|
-
"""
|
|
49
|
-
Initialises AES cipher with ``cipher_key``.
|
|
46
|
+
"""Initialises AES cipher with ``cipher_key``.
|
|
50
47
|
|
|
51
48
|
:param str cipher_key: 16, 24, or 32 bytes encoded as base64
|
|
52
49
|
"""
|
|
@@ -60,7 +57,6 @@ class AESCipher(Cipher):
|
|
|
60
57
|
|
|
61
58
|
def encrypt(self, plaintext: bytes) -> bytes:
|
|
62
59
|
"""Return ciphertext for given plaintext."""
|
|
63
|
-
|
|
64
60
|
# Construct AES-GCM cipher, with 96-bit nonce.
|
|
65
61
|
aesgcm = AESGCM(self.key)
|
|
66
62
|
nonce = AESCipher.random_bytes(12)
|
|
@@ -72,7 +68,6 @@ class AESCipher(Cipher):
|
|
|
72
68
|
|
|
73
69
|
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
74
70
|
"""Return plaintext for given ciphertext."""
|
|
75
|
-
|
|
76
71
|
# Split out the nonce, tag, and encrypted data.
|
|
77
72
|
nonce = ciphertext[:12]
|
|
78
73
|
if len(nonce) != 12:
|
|
@@ -62,7 +62,7 @@ class singledispatchmethod(_singledispatchmethod[_T]): # noqa: N801
|
|
|
62
62
|
# cls.__wrapped__ = cls.__func__
|
|
63
63
|
|
|
64
64
|
try:
|
|
65
|
-
return self.dispatcher.register(cast(type[Any], cls), func=method)
|
|
65
|
+
return self.dispatcher.register(cast("type[Any]", cls), func=method)
|
|
66
66
|
except NameError:
|
|
67
67
|
self.deferred_registrations.append(
|
|
68
68
|
(cls, method) # pyright: ignore [reportArgumentType]
|
|
@@ -73,7 +73,7 @@ class singledispatchmethod(_singledispatchmethod[_T]): # noqa: N801
|
|
|
73
73
|
def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]:
|
|
74
74
|
for registered_cls, registered_method in self.deferred_registrations:
|
|
75
75
|
self.dispatcher.register(
|
|
76
|
-
cast(type[Any], registered_cls), func=registered_method
|
|
76
|
+
cast("type[Any]", registered_cls), func=registered_method
|
|
77
77
|
)
|
|
78
78
|
self.deferred_registrations = []
|
|
79
79
|
return super().__get__(obj, cls=cls)
|