コード例 #1
0
def test_construct_message_from_either_event_or_command():
    identifier = str(uuid4())
    command = Register(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")

    message = Message.to_message(command)

    assert message is not None
    assert type(message) is Message

    # Verify Message Content
    assert message.type == fully_qualified_name(Register)
    assert message.stream_name == f"{User.meta_.stream_name}:command-{identifier}"
    assert message.metadata.kind == "COMMAND"
    assert message.data == command.to_dict()

    event = Registered(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")

    # This simulates the call by UnitOfWork
    message = Message.to_message(event)

    assert message is not None
    assert type(message) is Message

    # Verify Message Content
    assert message.type == fully_qualified_name(Registered)
    assert message.stream_name == f"{User.meta_.stream_name}-{identifier}"
    assert message.metadata.kind == "EVENT"
    assert message.data == event.to_dict()
    assert message.time is None
コード例 #2
0
async def test_that_any_message_can_be_handled_with_any_handler(test_domain):
    test_domain.register(User)
    test_domain.register(Registered)
    test_domain.register(Post)
    test_domain.register(Created)
    test_domain.register(SystemMetrics)

    identifier = str(uuid4())
    registered = Registered(
        id=identifier,
        email="*****@*****.**",
        name="John Doe",
        password_hash="hash",
    )
    user = User(**registered.to_dict())
    message1 = Message.to_aggregate_event_message(user, registered)

    post_identifier = str(uuid4())
    created = Created(id=post_identifier, topic="Foo", content="Bar")
    post = Post(**created.to_dict())
    test_domain.event_store.store.append_aggregate_event(post, created)
    message2 = Message.to_aggregate_event_message(post, created)

    engine = Engine(domain=test_domain, test_mode=True)
    await engine.handle_message(SystemMetrics, message1)
    await engine.handle_message(SystemMetrics, message2)

    global counter
    assert counter == 2
コード例 #3
0
def test_construct_message_from_event(test_domain):
    identifier = str(uuid4())
    event = Registered(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")
    user = User(**event.to_dict())

    # This simulates the call by UnitOfWork
    message = Message.to_aggregate_event_message(user, event)

    assert message is not None
    assert type(message) is Message

    # Verify Message Content
    assert message.type == fully_qualified_name(Registered)
    assert message.stream_name == f"{User.meta_.stream_name}-{identifier}"
    assert message.metadata.kind == "EVENT"
    assert message.metadata.owner == test_domain.domain_name
    assert message.data == event.to_dict()
    assert message.time is None
    assert message.expected_version == user._version

    # Verify Message Dict
    message_dict = message.to_dict()

    assert message_dict["type"] == fully_qualified_name(Registered)
    assert message_dict["metadata"]["kind"] == "EVENT"
    assert message_dict["metadata"]["owner"] == test_domain.domain_name
    assert message_dict[
        "stream_name"] == f"{User.meta_.stream_name}-{identifier}"
    assert message_dict["data"] == event.to_dict()
    assert message_dict["time"] is None
    assert message_dict["expected_version"] == user._version
コード例 #4
0
def test_construct_message_from_command(test_domain):
    identifier = str(uuid4())
    command = Register(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")

    message = Message.to_command_message(command)

    assert message is not None
    assert type(message) is Message

    # Verify Message Content
    assert message.type == fully_qualified_name(Register)
    assert message.stream_name == f"{User.meta_.stream_name}:command-{identifier}"
    assert message.metadata.kind == "COMMAND"
    assert message.metadata.owner == test_domain.domain_name
    assert message.data == command.to_dict()
    assert message.time is None

    # Verify Message Dict
    message_dict = message.to_dict()
    assert message_dict["type"] == fully_qualified_name(Register)
    assert message_dict["metadata"]["kind"] == "COMMAND"
    assert message_dict["metadata"]["owner"] == test_domain.domain_name
    assert (message_dict["stream_name"] ==
            f"{User.meta_.stream_name}:command-{identifier}")
    assert message_dict["data"] == command.to_dict()
    assert message_dict["time"] is None
コード例 #5
0
async def test_that_an_event_handler_can_be_associated_with_an_all_stream(test_domain):
    test_domain.register(User)
    test_domain.register(Registered)
    test_domain.register(UserEventHandler, aggregate_cls=User)

    identifier = str(uuid4())
    user = User(
        id=identifier,
        email="*****@*****.**",
        name="John Doe",
        password_hash="hash",
    )
    event = Registered(
        id=identifier,
        email="*****@*****.**",
        name="John Doe",
        password_hash="hash",
    )
    message = Message.to_aggregate_event_message(user, event)

    engine = Engine(domain=test_domain, test_mode=True)
    await engine.handle_message(UserEventHandler, message)

    global counter
    assert counter == 1
コード例 #6
0
def registered_event_message(user_id):
    return Message.to_event_message(
        Registered(
            user_id=user_id,
            email="*****@*****.**",
            name="John Doe",
        ))
コード例 #7
0
def register_command_message(user_id):
    return Message.to_command_message(
        Register(
            user_id=user_id,
            email="*****@*****.**",
            name="John Doe",
        ))
コード例 #8
0
ファイル: event_store.py プロジェクト: proteanhq/protean
    def append_event(self, event: BaseEvent) -> int:
        message = Message.to_event_message(event)

        return self._write(
            message.stream_name,
            message.type,
            message.data,
            metadata=message.metadata.to_dict(),
        )
コード例 #9
0
ファイル: event_store.py プロジェクト: proteanhq/protean
    def append_command(self, command: BaseCommand) -> int:
        message = Message.to_command_message(command)

        return self._write(
            message.stream_name,
            message.type,
            message.data,
            metadata=message.metadata.to_dict(),
        )
コード例 #10
0
def test_origin_stream_name_in_command_from_event(user_id):
    g.message_in_context = registered_event_message(user_id)
    command_message = Message.to_command_message(
        Register(
            user_id=user_id,
            email="*****@*****.**",
            name="John Doe",
        ))

    assert command_message.metadata.origin_stream_name == f"user-{user_id}"
コード例 #11
0
def test_construct_command_from_message(test_domain):
    identifier = str(uuid4())
    command = Register(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")
    message = Message.to_command_message(command)

    reconstructed_command = message.to_object()
    assert isinstance(reconstructed_command, Register)
    assert reconstructed_command.id == identifier
コード例 #12
0
def test_construct_event_from_message(test_domain):
    identifier = str(uuid4())
    event = Registered(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")
    user = User(**event.to_dict())
    message = Message.to_aggregate_event_message(user, event)

    reconstructed_event = message.to_object()
    assert isinstance(reconstructed_event, Registered)
    assert reconstructed_event.id == identifier
コード例 #13
0
def test_origin_stream_name_in_event_from_command_without_origin_stream_name(
        user_id):
    g.message_in_context = register_command_message(user_id)

    event_message = Message.to_event_message(
        Registered(
            user_id=user_id,
            email="*****@*****.**",
            name="John Doe",
        ))
    assert event_message.metadata.origin_stream_name is None
コード例 #14
0
    def last_event_of_type(self,
                           event_cls: Type[BaseEvent],
                           stream_name: str = None) -> BaseEvent:
        stream_name = stream_name or "$all"
        events = [
            event for event in self.domain.event_store.store._read(stream_name)
            if event["type"] == fqn(event_cls)
        ]

        return Message.from_dict(
            events[-1]).to_object() if len(events) > 0 else None
コード例 #15
0
async def test_message_filtering_for_event_handlers_with_defined_origin_stream(
    test_domain, ):
    test_domain.register(UserEventHandler, aggregate_cls=User)
    test_domain.register(EmailEventHandler,
                         stream_name="email",
                         source_stream="user")

    engine = Engine(test_domain, test_mode=True)
    email_event_handler_subscription = engine._subscriptions[fqn(
        EmailEventHandler)]

    identifier = str(uuid4())
    user = User(id=identifier, email="*****@*****.**", name="John Doe")
    email = Email(id=identifier, email="*****@*****.**")

    # Construct 3 dummy messages and modify Sent message to have originated from the user stream
    messages = [
        Message.to_aggregate_event_message(
            user,
            Registered(id=identifier,
                       email="*****@*****.**",
                       name="John Doe")),
        Message.to_aggregate_event_message(
            user, Activated(id=identifier, activated_at=datetime.utcnow())),
        Message.to_aggregate_event_message(
            email, Sent(email="*****@*****.**",
                        sent_at=datetime.utcnow())),
    ]
    messages[2].metadata.origin_stream_name = f"user-{identifier}"

    # Mock `read` method and have it return the 3 messages
    mock_store_read = mock.Mock()
    mock_store_read.return_value = messages
    email_event_handler_subscription.store.read = mock_store_read

    filtered_messages = (
        await email_event_handler_subscription.get_next_batch_of_messages())

    assert len(filtered_messages) == 1
    assert filtered_messages[0].type == fqn(Sent)
コード例 #16
0
def test_applying_events():
    identifier = str(uuid4())

    registered = UserRegistered(user_id=identifier,
                                name="John Doe",
                                email="*****@*****.**")
    activated = UserActivated(user_id=identifier)
    renamed = UserRenamed(user_id=identifier, name="Jane Doe")

    user = User.register(**registered.to_dict())

    msg_registered = Message.to_aggregate_event_message(user, registered)
    user._apply(msg_registered.to_dict())
    assert user.status == UserStatus.INACTIVE.value

    msg_activated = Message.to_aggregate_event_message(user, activated)
    user._apply(msg_activated.to_dict())
    assert user.status == UserStatus.ACTIVE.value

    msg_renamed = Message.to_aggregate_event_message(user, renamed)
    user._apply(msg_renamed.to_dict())
    assert user.name == "Jane Doe"
コード例 #17
0
ファイル: event_store.py プロジェクト: proteanhq/protean
    def read(
        self,
        stream_name: str,
        sql: str = None,
        position: int = 0,
        no_of_messages: int = 1000,
    ):
        raw_messages = self._read(stream_name,
                                  sql=sql,
                                  position=position,
                                  no_of_messages=no_of_messages)

        messages = []
        for raw_message in raw_messages:
            messages.append(Message.from_dict(raw_message))

        return messages
コード例 #18
0
ファイル: event_store.py プロジェクト: proteanhq/protean
    def append_aggregate_event(self, aggregate: BaseEventSourcedAggregate,
                               event: BaseEvent) -> int:
        message = Message.to_aggregate_event_message(aggregate, event)

        position = self._write(
            message.stream_name,
            message.type,
            message.data,
            metadata=message.metadata.to_dict(),
            expected_version=message.expected_version,
        )

        # Increment aggregate's version as we process events
        #    to correctly handle expected version
        aggregate._version += 1

        return position
コード例 #19
0
def test_origin_stream_name_in_aggregate_event_from_command_without_origin_stream_name(
    user_id, ):
    g.message_in_context = register_command_message(user_id)
    user = User(
        id=user_id,
        email="*****@*****.**",
        name="John Doe",
    )
    event_message = Message.to_aggregate_event_message(
        user,
        Register(
            user_id=user_id,
            email="*****@*****.**",
            name="John Doe",
        ),
    )

    assert event_message.metadata.origin_stream_name is None
コード例 #20
0
async def test_handler_invocation(test_domain):
    test_domain.register(User)
    test_domain.register(Register)
    test_domain.register(Activate)
    test_domain.register(UserCommandHandler, aggregate_cls=User)

    identifier = str(uuid4())
    command = Register(
        user_id=identifier,
        email="*****@*****.**",
    )
    message = Message.to_command_message(command)

    engine = Engine(domain=test_domain, test_mode=True)
    await engine.handle_message(UserCommandHandler, message)

    global counter
    assert counter == 1
コード例 #21
0
ファイル: __init__.py プロジェクト: proteanhq/protean
    def publish(self, object: BaseEvent) -> None:
        """Publish an object to all registered brokers"""
        if self._brokers is None:
            self._initialize()

        message = Message.to_message(object)

        # Follow a naive strategy and dispatch event directly to message broker
        #   If the operation is enclosed in a Unit of Work, delegate the responsibility
        #   of publishing the message to the UoW
        if current_uow:
            logger.debug(f"Recording {object.__class__.__name__} "
                         f"with values {object.to_dict()} in {current_uow}")
            current_uow.register_message(message)
        else:
            logger.debug(
                f"Publishing {object.__class__.__name__} with values {object.to_dict()}"
            )
            for _, broker in self._brokers.items():
                broker.publish(message)
コード例 #22
0
def test_construct_message_from_command_without_identifier():
    """Test that a new UUID is used as identifier when there is no explicit identifier specified"""
    identifier = str(uuid4())
    command = SendEmailCommand(to="*****@*****.**",
                               subject="Foo",
                               content="Bar")

    message = Message.to_command_message(command)

    assert message is not None
    assert type(message) is Message

    message_dict = message.to_dict()
    identifier = message_dict["stream_name"].split(
        f"{SendEmail.meta_.stream_name}:command-", 1)[1]

    try:
        UUID(identifier, version=4)
    except ValueError:
        pytest.fail("Command identifier is not a valid UUID")
コード例 #23
0
    def events_of_type(self,
                       event_cls: Type[BaseEvent],
                       stream_name: str = None) -> List[BaseEvent]:
        """Read events of a specific type in a given stream.

        This is a utility method, especially useful for testing purposes, that retrives events of a
        specific type from the event store.

        If no stream is specified, events of the requested type will be retrieved from all streams.

        :param event_cls: Class of the event type to be retrieved
        :param stream_name: Stream from which events are to be retrieved
        :type event_cls: BaseEvent Class
        :type stream_name: String, optional, default is `None`
        :return: A list of events of `event_cls` type
        :rtype: list
        """
        stream_name = stream_name or "$all"
        return [
            Message.from_dict(event).to_object()
            for event in self.domain.event_store.store._read(stream_name)
            if event["type"] == fqn(event_cls)
        ]
コード例 #24
0
    def get_next(self) -> Message:
        bytes_message = self.redis_instance.lpop("messages")
        if bytes_message:
            return Message(json.loads(bytes_message))

        return None
コード例 #25
0
 def publish(self, message: Message) -> None:
     # FIXME Accept configuration for database and list name
     self.redis_instance.rpush("messages", json.dumps(message.to_dict()))
コード例 #26
0
ファイル: inline.py プロジェクト: proteanhq/protean
    def publish(self, message: Message) -> None:
        initiator_obj = message.to_object()

        for subscriber in self._subscribers[fully_qualified_name(
                initiator_obj.__class__)]:
            subscriber()(message.data)
コード例 #27
0
ファイル: event_store.py プロジェクト: proteanhq/protean
 def read_last_message(self, stream_name) -> Message:
     # FIXME Rename to read_last_stream_message
     raw_message = self._read_last_message(stream_name)
     return Message.from_dict(raw_message)