async def test_aggregate_should_decode_to_json_filtering_by_fields(dbsession): # arrange class BankAccount(aggregates.Aggregate): id = aggregates.IntegerField() owner = aggregates.CharField() balance = aggregates.IntegerField() def apply_bank_account_created(self, event): self.id = event.id self.owner = event.owner self.balance = 0 def apply_deposit_performed(self, event): self.balance += event.amount events = EventStream( [BankAccountCreated(id=123, owner="John Doe", version=0)]) deposit_performed = DepositPerformed(amount=20) # act bank_account = BankAccount() bank_account.fetch_events(events) bank_account.dispatch(deposit_performed) result = bank_account.json(only=("id", )) # assert expected_result = '{"id": 123}' assert isinstance(result, str) assert result == expected_result
async def test_aggregate_should_return_stored_events(dbsession): # arrange class BankAccount(aggregates.Aggregate): id = aggregates.IntegerField() owner = aggregates.CharField() balance = aggregates.IntegerField() def apply_bank_account_created(self, event): self.id = event.get("id") self.owner = event.get("owner") self.balance = 0 def apply_deposit_performed(self, event): self.balance += event.get("amount") events = EventStream( [BankAccountCreated(id=123, owner="John Doe", version=0)]) deposit_performed = DepositPerformed(amount=20) # act bank_account = BankAccount() bank_account.fetch_events(events) bank_account.dispatch(deposit_performed) result = list(bank_account.stored_events()) # assert assert len(result) == 1 assert result[0].version == 0 assert result[0].id == 123 assert result[0].owner == "John Doe"
async def test_aggregate_should_be_created_from_events(dbsession): # arrange class BankAccount(aggregates.Aggregate): id = aggregates.IntegerField() owner = aggregates.CharField() balance = aggregates.IntegerField() def apply_bank_account_created(self, event): self.id = event.get("id") self.owner = event.get("owner") self.balance = 0 def apply_deposit_performed(self, event): self.balance += event.get("amount") events = EventStream( [BankAccountCreated(id=123, owner="John Doe", version=0)]) deposit_performed = DepositPerformed(amount=20) # act bank_account = BankAccount.from_stream(events) bank_account.dispatch(deposit_performed) # assert assert bank_account.version == 0 assert bank_account.current_version == 1 assert bank_account.id == 123 assert bank_account.owner == "John Doe" assert bank_account.balance == 20
async def test_aggregate_should_return_new_events(dbsession): # arrange class BankAccount(aggregates.Aggregate): id = aggregates.IntegerField() owner = aggregates.CharField() balance = aggregates.IntegerField() def apply_bank_account_created(self, event): self.id = event.get('id') self.owner = event.get('owner') self.balance = 0 def apply_deposit_performed(self, event): self.balance += event.get('amount') events = EventStream( [BankAccountCreated( id=123, owner='John Doe', version=0, )]) deposit_performed = DepositPerformed(amount=20, ) # act bank_account = BankAccount() bank_account.fetch_events(events) bank_account.dispatch(deposit_performed) result = list(bank_account.get_events()) # assert assert len(result) == 1 assert result[0].version == 1 assert result[0].amount == 20
async def test_aggregate_should_apply_event_after_load_events(dbsession): # arrange class BankAccount(aggregates.Aggregate): id = aggregates.IntegerField() owner = aggregates.CharField() balance = aggregates.IntegerField() def apply_bank_account_created(self, event): self.id = event.get('id') self.owner = event.get('owner') self.balance = 0 def apply_deposit_performed(self, event): self.balance += event.get('amount') events = EventStream( [BankAccountCreated( id=123, owner='John Doe', version=0, )]) deposit_performed = DepositPerformed(amount=20, ) # act bank_account = BankAccount() bank_account.fetch_events(events) bank_account.dispatch(deposit_performed) # assert assert bank_account.version == 0 assert bank_account.current_version == 1 assert bank_account.id == 123 assert bank_account.owner == 'John Doe' assert bank_account.balance == 20
def __init__(self): super().__init__() self._all_events = EventStream() self._events = EventStream() self._stored_events = EventStream()
class Aggregate(FieldMapping, metaclass=AggregateMeta): def __init__(self): super().__init__() self._all_events = EventStream() self._events = EventStream() self._stored_events = EventStream() def all_events(self): return self._all_events def stored_events(self): return self._stored_events def get_events(self): return self._events def notify_save(self, new_version): self._events.clear() self._events.initial_version = new_version self._all_events.initial_version = new_version self._stored_events.initial_version = new_version def fetch_events(self, events: EventStream): self._stored_events = deepcopy(events) self._all_events = deepcopy(self._stored_events) self._events.initial_version = events.initial_version for event in events: self.dispatch(event, flush=False) def apply(self, event): event_name = underscore(event.__class__.__name__) method_name = 'apply_{0}'.format(event_name) try: method = getattr(self, method_name) method(event) except AggregateError: msg = "The command for '{}' is not defined".format( event.__class__.__name__) raise CommandError(msg) def dispatch(self, events, flush=True): if isinstance(events, list): received_events = list(events) else: received_events = [events] for event in received_events: self.apply(event) if flush: self._events.add(event) self._all_events.add(event) @property def current_version(self): return self._all_events.current_version @property def version(self): return self._all_events.initial_version @classmethod def from_stream(cls, stream): self = cls() self.fetch_events(stream) return self async def save(self): return await self.objects.save(self.get_pk(), self.get_events(), self.notify_save) async def refresh_from_db(self): stream = await self.objects.get_stream(self.get_pk()) self.fetch_events(stream) return self def get_pk(self): primary_keys = list(self.primary_keys().values()) if not primary_keys: msg = "Nothing primary key defined for '{}'".format( self.__class__.__name__) raise AggregateError(msg) elif len(primary_keys) > 1: msg = "Many primary keys defined for '{}'".format( self.__class__.__name__) raise AggregateError(msg) return primary_keys[0]