Esempio n. 1
0
class TimestampSequencedItemTestCase(ActiveRecordStrategyTestCase):
    EXAMPLE_EVENT_TOPIC1 = topic_from_domain_class(TimestampedEventExample1)
    EXAMPLE_EVENT_TOPIC2 = topic_from_domain_class(TimestampedEventExample2)

    def construct_positions(self):
        t1 = time()
        return t1, t1 + 0.00001, t1 + 0.00002
    def test_with_timestamped_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(
            sequenced_item_class=SequencedItem,
            sequence_id_attr_name='originator_id',
            position_attr_name='timestamp'
        )
        before = time()
        sleep(0.000001)  # Avoid test failing due to timestamp having limited precision.
        event2 = Event2(originator_id='entity2')
        sleep(0.000001)  # Avoid test failing due to timestamp having limited precision.
        after = time()

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.to_sequenced_item(event2)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertGreater(sequenced_item.position, before)
        self.assertLess(sequenced_item.position, after)
        self.assertEqual(sequenced_item.sequence_id, 'entity2')
        self.assertEqual(sequenced_item.topic, topic_from_domain_class(Event2))
        self.assertTrue(sequenced_item.data)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            data=sequenced_item.data,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.from_sequenced_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event2)
        self.assertEqual(domain_event.originator_id, event2.originator_id)
        self.assertEqual(domain_event.timestamp, event2.timestamp)
    def test_with_versioned_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(
            sequenced_item_class=SequencedItem,
            sequence_id_attr_name='originator_id',
            position_attr_name='originator_version'
        )
        entity_id1 = uuid4()
        event1 = Event1(originator_id=entity_id1, originator_version=101)

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.to_sequenced_item(event1)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertEqual(sequenced_item.position, 101)
        self.assertEqual(sequenced_item.sequence_id, entity_id1)
        self.assertEqual(sequenced_item.topic, topic_from_domain_class(Event1))
        self.assertTrue(sequenced_item.data)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            data=sequenced_item.data,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.from_sequenced_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event1)
        self.assertEqual(domain_event.originator_id, event1.originator_id)
        self.assertEqual(domain_event.originator_version, event1.originator_version)
    def test_with_versioned_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(sequenced_item_class=SequencedItem,
                                     sequence_id_attr_name='originator_id',
                                     position_attr_name='originator_version')
        entity_id1 = uuid4()
        event1 = Event1(originator_id=entity_id1, originator_version=101)

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.to_sequenced_item(event1)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertEqual(sequenced_item.position, 101)
        self.assertEqual(sequenced_item.sequence_id, entity_id1)
        self.assertEqual(sequenced_item.topic, topic_from_domain_class(Event1))
        self.assertTrue(sequenced_item.data)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            data=sequenced_item.data,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.from_sequenced_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event1)
        self.assertEqual(domain_event.originator_id, event1.originator_id)
        self.assertEqual(domain_event.originator_version,
                         event1.originator_version)
Esempio n. 5
0
def take_snapshot(entity, timestamp=None):
    # Make the 'stored entity ID' for the entity, it is used as the Snapshot 'entity ID'.

    # Create the snapshot event.
    snapshot = Snapshot(
        entity_id=entity.id,
        timestamp=timestamp,
        topic=topic_from_domain_class(entity.__class__),
        # Todo: This should be a deepcopy.
        state=entity.__dict__.copy(),
    )
    publish(snapshot)

    # Return the event.
    return snapshot
Esempio n. 6
0
def serialize_domain_event(domain_event,
                           json_encoder_cls=None,
                           without_json=False,
                           with_uuid1=False,
                           cipher=None,
                           always_encrypt=False):
    """
    Serializes a domain event into a stored event.
    """
    # assert isinstance(domain_event, DomainEvent)

    # Copy the state of the domain event.
    event_attrs = domain_event.__dict__.copy()

    # Get, or make, the domain event ID.
    if with_uuid1:
        event_id = event_attrs.pop('domain_event_id')
    else:
        event_id = uuid.uuid4().hex

    # Make stored entity ID and topic.
    stored_entity_id = make_stored_entity_id(
        id_prefix_from_event(domain_event), domain_event.entity_id)
    event_topic = topic_from_domain_class(type(domain_event))

    # Serialise event attributes to JSON, optionally encrypted with cipher.
    if not without_json:

        if json_encoder_cls is None:
            json_encoder_cls = ObjectJSONEncoder

        event_attrs = json.dumps(event_attrs,
                                 separators=(',', ':'),
                                 sort_keys=True,
                                 cls=json_encoder_cls)

        if always_encrypt or domain_event.__class__.always_encrypt:
            if cipher is None:
                raise ValueError("Can't encrypt without a cipher")
            event_attrs = cipher.encrypt(event_attrs)

    # Return a named tuple.
    return StoredEvent(
        event_id=event_id,
        stored_entity_id=stored_entity_id,
        event_topic=event_topic,
        event_attrs=event_attrs,
    )
Esempio n. 7
0
def take_snapshot(entity, at_event_id):
    # Make the 'stored entity ID' for the entity, it is used as the Snapshot 'entity ID'.
    id_prefix = id_prefix_from_entity(entity)
    stored_entity_id = make_stored_entity_id(id_prefix, entity.id)

    # Create the snapshot event.
    snapshot = Snapshot(
        entity_id=stored_entity_id,
        domain_event_id=at_event_id,
        topic=topic_from_domain_class(entity.__class__),
        attrs=entity.__dict__.copy(),
    )
    publish(snapshot)

    # Return the event.
    return snapshot
Esempio n. 8
0
def take_snapshot(entity, at_event_id):
    # Make the 'stored entity ID' for the entity, it is used as the Snapshot 'entity ID'.
    id_prefix = id_prefix_from_entity(entity)
    stored_entity_id = make_stored_entity_id(id_prefix, entity.id)

    # Create the snapshot event.
    snapshot = Snapshot(
        entity_id=stored_entity_id,
        domain_event_id=at_event_id,
        topic=topic_from_domain_class(entity.__class__),
        attrs=entity.__dict__.copy(),
    )
    publish(snapshot)

    # Return the event.
    return snapshot
Esempio n. 9
0
 def default(self, obj):
     if isinstance(obj, datetime.datetime):
         return {'ISO8601_datetime': obj.strftime('%Y-%m-%dT%H:%M:%S.%f%z')}
     elif isinstance(obj, datetime.date):
         return {'ISO8601_date': obj.isoformat()}
     elif isinstance(obj, UUID):
         return {'UUID': obj.hex}
     elif hasattr(obj, '__class__') and hasattr(obj, '__dict__'):
         topic = topic_from_domain_class(obj.__class__)
         state = obj.__dict__.copy()
         return {
             '__class__': {
                 'topic': topic,
                 'state': state,
             }
         }
     # Let the base class default method raise the TypeError.
     return JSONEncoder.default(self, obj)
Esempio n. 10
0
 def default(self, obj):
     if isinstance(obj, datetime.datetime):
         return {'ISO8601_datetime': obj.strftime('%Y-%m-%dT%H:%M:%S.%f%z')}
     elif isinstance(obj, datetime.date):
         return {'ISO8601_date': obj.isoformat()}
     elif isinstance(obj, UUID):
         return {'UUID': obj.hex}
     elif hasattr(obj, '__class__') and hasattr(obj, '__dict__'):
         topic = topic_from_domain_class(obj.__class__)
         state = obj.__dict__.copy()
         return {
             '__class__': {
                 'topic': topic,
                 'state': state,
             }
         }
     # Let the base class default method raise the TypeError.
     return JSONEncoder.default(self, obj)
Esempio n. 11
0
    def take_snapshot(self, entity_id, entity, last_event_version):
        """
        Takes a snapshot by instantiating and publishing a Snapshot domain event.

        :rtype: Snapshot
        """
        # Create the snapshot event.
        snapshot = Snapshot(
            originator_id=entity_id,
            originator_version=last_event_version,
            topic=topic_from_domain_class(entity.__class__),
            state=None if entity is None else deepcopy(entity.__dict__)
        )

        # Publish the snapshot event.
        publish(snapshot)

        # Return the snapshot event.
        return snapshot
Esempio n. 12
0
    def construct_item_args(self, domain_event):
        """
        Constructs attributes of a sequenced item from the given domain event.
        """
        # Identify the sequence ID.
        sequence_id = getattr(domain_event, self.sequence_id_attr_name)

        # Identify the position in the sequence.
        position = getattr(domain_event, self.position_attr_name)

        # Construct the topic from the event class.
        topic = topic_from_domain_class(domain_event.__class__)

        # Serialise the state of the event.
        is_encrypted = self.is_encrypted(domain_event.__class__)
        data = self.serialize_event_attrs(domain_event.__dict__, is_encrypted=is_encrypted)

        other_args = tuple((getattr(domain_event, name) for name in self.other_attr_names))
        return (sequence_id, position, topic, data) + other_args
Esempio n. 13
0
def serialize_domain_event(domain_event, json_encoder_cls=None, without_json=False, with_uuid1=False, cipher=None,
                           always_encrypt=False):
    """
    Serializes a domain event into a stored event.
    """
    # assert isinstance(domain_event, DomainEvent)

    # Copy the state of the domain event.
    event_attrs = domain_event.__dict__.copy()

    # Get, or make, the domain event ID.
    if with_uuid1:
        event_id = event_attrs.pop('domain_event_id')
    else:
        event_id = uuid.uuid4().hex

    # Make stored entity ID and topic.
    stored_entity_id = make_stored_entity_id(id_prefix_from_event(domain_event), domain_event.entity_id)
    event_topic = topic_from_domain_class(type(domain_event))

    # Serialise event attributes to JSON, optionally encrypted with cipher.
    if not without_json:

        if json_encoder_cls is None:
            json_encoder_cls = ObjectJSONEncoder

        event_attrs = json.dumps(event_attrs, separators=(',', ':'), sort_keys=True, cls=json_encoder_cls)

        if always_encrypt or domain_event.__class__.always_encrypt:
            if cipher is None:
                raise ValueError("Can't encrypt without a cipher")
            event_attrs = cipher.encrypt(event_attrs)

    # Return a named tuple.
    return StoredEvent(
        event_id=event_id,
        stored_entity_id=stored_entity_id,
        event_topic=event_topic,
        event_attrs=event_attrs,
    )
    def test_recreate_domain_event(self):
        stored_event = StoredEvent(
            event_id='1',
            stored_entity_id='entity1',
            event_topic='eventsourcing.domain.model.events#DomainEvent',
            event_attrs=
            '{"a":1,"b":2,"c":{"ISO8601_datetime":"2015-09-08T16:20:50.577429"},"d":{"ISO8601_datetime":"2015-09-08T16:20:50.577429+0000"},"domain_event_id":3,"e":{"ISO8601_date":"2015-09-08"},"entity_id":"entity1","entity_version":0}'
        )
        domain_event = deserialize_domain_event(
            stored_event, json_decoder_cls=ObjectJSONDecoder)
        self.assertIsInstance(domain_event, DomainEvent)
        self.assertEqual('entity1', domain_event.entity_id)
        self.assertEqual(1, domain_event.a)
        self.assertEqual(2, domain_event.b)
        datetime_now_timezoneaware = datetime.datetime(2015,
                                                       9,
                                                       8,
                                                       16,
                                                       20,
                                                       50,
                                                       577429,
                                                       tzinfo=utc_timezone)
        # self.assertEqual(datetime_now, domain_event.c)
        self.assertEqual(datetime_now_timezoneaware, domain_event.d)
        date_now = datetime.date(2015, 9, 8)
        self.assertEqual(date_now, domain_event.e)
        self.assertEqual(3, domain_event.domain_event_id)

        # Check the TypeError is raised.
        stored_event = StoredEvent(
            event_id='1',
            stored_entity_id='entity1',
            event_topic=topic_from_domain_class(NotADomainEvent),
            event_attrs=
            '{"a":1,"b":2,"stored_entity_id":"entity1","timestamp":3}')
        self.assertRaises(ValueError,
                          deserialize_domain_event,
                          stored_event,
                          json_decoder_cls=ObjectJSONDecoder)
    def test_recreate_domain_event(self):
        stored_event = StoredEvent(event_id='1',
                                   stored_entity_id='entity1',
                                   event_topic='eventsourcing.domain.model.events#DomainEvent',
                                   event_attrs='{"a":1,"b":2,"c":{"ISO8601_datetime":"2015-09-08T16:20:50.577429"},"d":{"ISO8601_datetime":"2015-09-08T16:20:50.577429+0000"},"domain_event_id":3,"e":{"ISO8601_date":"2015-09-08"},"entity_id":"entity1","entity_version":0}')
        domain_event = deserialize_domain_event(stored_event, json_decoder_cls=ObjectJSONDecoder)
        self.assertIsInstance(domain_event, DomainEvent)
        self.assertEqual('entity1', domain_event.entity_id)
        self.assertEqual(1, domain_event.a)
        self.assertEqual(2, domain_event.b)
        datetime_now_timezoneaware = datetime.datetime(2015, 9, 8, 16, 20, 50, 577429, tzinfo=utc_timezone)
        # self.assertEqual(datetime_now, domain_event.c)
        self.assertEqual(datetime_now_timezoneaware, domain_event.d)
        date_now = datetime.date(2015, 9, 8)
        self.assertEqual(date_now, domain_event.e)
        self.assertEqual(3, domain_event.domain_event_id)

        # Check the TypeError is raised.
        stored_event = StoredEvent(event_id='1',
                                   stored_entity_id='entity1',
                                   event_topic=topic_from_domain_class(NotADomainEvent),
                                   event_attrs='{"a":1,"b":2,"stored_entity_id":"entity1","timestamp":3}')
        self.assertRaises(ValueError, deserialize_domain_event, stored_event, json_decoder_cls=ObjectJSONDecoder)
    def test_with_timestamped_entity_event(self):
        # Setup the mapper, and create an event.
        mapper = SequencedItemMapper(sequenced_item_class=SequencedItem,
                                     sequence_id_attr_name='originator_id',
                                     position_attr_name='timestamp')
        before = time()
        sleep(
            0.000001
        )  # Avoid test failing due to timestamp having limited precision.
        event2 = Event2(originator_id='entity2')
        sleep(
            0.000001
        )  # Avoid test failing due to timestamp having limited precision.
        after = time()

        # Check to_sequenced_item() method results in a sequenced item.
        sequenced_item = mapper.to_sequenced_item(event2)
        self.assertIsInstance(sequenced_item, SequencedItem)
        self.assertGreater(sequenced_item.position, before)
        self.assertLess(sequenced_item.position, after)
        self.assertEqual(sequenced_item.sequence_id, 'entity2')
        self.assertEqual(sequenced_item.topic, topic_from_domain_class(Event2))
        self.assertTrue(sequenced_item.data)

        # Use the returned values to create a new sequenced item.
        sequenced_item_copy = SequencedItem(
            sequence_id=sequenced_item.sequence_id,
            position=sequenced_item.position,
            topic=sequenced_item.topic,
            data=sequenced_item.data,
        )

        # Check from_sequenced_item() returns an event.
        domain_event = mapper.from_sequenced_item(sequenced_item_copy)
        self.assertIsInstance(domain_event, Event2)
        self.assertEqual(domain_event.originator_id, event2.originator_id)
        self.assertEqual(domain_event.timestamp, event2.timestamp)
Esempio n. 17
0
 def topic_from_domain_class(self, domain_class):
     return topic_from_domain_class(domain_class)
Esempio n. 18
0
class IntegerSequencedItemTestCase(ActiveRecordStrategyTestCase):
    EXAMPLE_EVENT_TOPIC1 = topic_from_domain_class(ExampleVersionEntityEvent1)
    EXAMPLE_EVENT_TOPIC2 = topic_from_domain_class(ExampleVersionEntityEvent2)

    def construct_positions(self):
        return 0, 1, 2