diff --git a/baseline.xml b/baseline.xml
index 258e72349..6f24145b1 100644
--- a/baseline.xml
+++ b/baseline.xml
@@ -28,6 +28,11 @@
Headers
+
+
+
+
+
$aggregate::metadata()
diff --git a/src/EventBus/Serializer/DeserializeFailed.php b/src/EventBus/Serializer/DeserializeFailed.php
index b34f0a917..0a7aea809 100644
--- a/src/EventBus/Serializer/DeserializeFailed.php
+++ b/src/EventBus/Serializer/DeserializeFailed.php
@@ -4,7 +4,6 @@
namespace Patchlevel\EventSourcing\EventBus\Serializer;
-use Patchlevel\EventSourcing\EventBus\Message;
use RuntimeException;
use function get_debug_type;
@@ -17,12 +16,11 @@ public static function decodeFailed(): self
return new self('Error while decoding message');
}
- public static function invalidMessage(mixed $value): self
+ public static function invalidData(mixed $value): self
{
return new self(
sprintf(
- 'Value should me an instance of %s, but is %s',
- Message::class,
+ 'Invalid data: %s',
get_debug_type($value),
),
);
diff --git a/src/EventBus/Serializer/EventSerializerMessageSerializer.php b/src/EventBus/Serializer/EventSerializerMessageSerializer.php
new file mode 100644
index 000000000..646c85307
--- /dev/null
+++ b/src/EventBus/Serializer/EventSerializerMessageSerializer.php
@@ -0,0 +1,71 @@
+eventSerializer->serialize($message->event());
+
+ return base64_encode(
+ serialize(
+ [
+ 'serializedEvent' => $serializedEvent,
+ 'headers' => $message->headers(),
+ ],
+ ),
+ );
+ }
+
+ public function deserialize(string $content): Message
+ {
+ $decodedString = base64_decode($content, true);
+
+ if (!is_string($decodedString)) {
+ throw DeserializeFailed::decodeFailed();
+ }
+
+ $data = unserialize(
+ $decodedString,
+ [
+ 'allowed_classes' => [
+ SerializedEvent::class,
+ DateTimeImmutable::class,
+ ],
+ ],
+ );
+
+ if (
+ !is_array($data)
+ || !isset($data['serializedEvent'], $data['headers'])
+ || !$data['serializedEvent'] instanceof SerializedEvent
+ || !is_array($data['headers'])
+ ) {
+ throw DeserializeFailed::invalidData($data);
+ }
+
+ $event = $this->eventSerializer->deserialize($data['serializedEvent']);
+
+ return Message::createWithHeaders($event, $data['headers']);
+ }
+}
diff --git a/src/EventBus/Serializer/PhpNativeMessageSerializer.php b/src/EventBus/Serializer/PhpNativeMessageSerializer.php
index 5c9663cdc..d2791d044 100644
--- a/src/EventBus/Serializer/PhpNativeMessageSerializer.php
+++ b/src/EventBus/Serializer/PhpNativeMessageSerializer.php
@@ -33,7 +33,7 @@ public function deserialize(string $content): Message
);
if (!$message instanceof Message) {
- throw DeserializeFailed::invalidMessage($message);
+ throw DeserializeFailed::invalidData($message);
}
return $message;
diff --git a/tests/Unit/EventBus/Serializer/EventSerializerMessageSerializerTest.php b/tests/Unit/EventBus/Serializer/EventSerializerMessageSerializerTest.php
new file mode 100644
index 000000000..6b34a521f
--- /dev/null
+++ b/tests/Unit/EventBus/Serializer/EventSerializerMessageSerializerTest.php
@@ -0,0 +1,113 @@
+withRecordedOn(new DateTimeImmutable('2020-01-01T20:00:00.000000+0100'));
+
+ $eventSerializer = $this->prophesize(EventSerializer::class);
+ $eventSerializer->serialize($event)->shouldBeCalledOnce()->willReturn(new SerializedEvent(
+ 'profile_visited',
+ '{id: foo}',
+ ));
+
+ $serializer = new EventSerializerMessageSerializer(
+ $eventSerializer->reveal(),
+ );
+
+ $content = $serializer->serialize($message);
+
+ self::assertEquals('YToyOntzOjE1OiJzZXJpYWxpemVkRXZlbnQiO086NTE6IlBhdGNobGV2ZWxcRXZlbnRTb3VyY2luZ1xTZXJpYWxpemVyXFNlcmlhbGl6ZWRFdmVudCI6Mjp7czo0OiJuYW1lIjtzOjE1OiJwcm9maWxlX3Zpc2l0ZWQiO3M6NzoicGF5bG9hZCI7czo5OiJ7aWQ6IGZvb30iO31zOjc6ImhlYWRlcnMiO2E6Mzp7czoxMDoicmVjb3JkZWRPbiI7TzoxNzoiRGF0ZVRpbWVJbW11dGFibGUiOjM6e3M6NDoiZGF0ZSI7czoyNjoiMjAyMC0wMS0wMSAyMDowMDowMC4wMDAwMDAiO3M6MTM6InRpbWV6b25lX3R5cGUiO2k6MTtzOjg6InRpbWV6b25lIjtzOjY6IiswMTowMCI7fXM6MTQ6Im5ld1N0cmVhbVN0YXJ0IjtiOjA7czo4OiJhcmNoaXZlZCI7YjowO319', $content);
+ }
+
+ public function testDeserialize(): void
+ {
+ $event = new ProfileVisited(
+ ProfileId::fromString('foo'),
+ );
+
+ $eventSerializer = $this->prophesize(EventSerializer::class);
+ $eventSerializer->deserialize(new SerializedEvent(
+ 'profile_visited',
+ '{id: foo}',
+ ))->shouldBeCalledOnce()->willReturn($event);
+
+ $serializer = new EventSerializerMessageSerializer(
+ $eventSerializer->reveal(),
+ );
+
+ $message = $serializer->deserialize('YToyOntzOjE1OiJzZXJpYWxpemVkRXZlbnQiO086NTE6IlBhdGNobGV2ZWxcRXZlbnRTb3VyY2luZ1xTZXJpYWxpemVyXFNlcmlhbGl6ZWRFdmVudCI6Mjp7czo0OiJuYW1lIjtzOjE1OiJwcm9maWxlX3Zpc2l0ZWQiO3M6NzoicGF5bG9hZCI7czo5OiJ7aWQ6IGZvb30iO31zOjc6ImhlYWRlcnMiO2E6Mzp7czoxMDoicmVjb3JkZWRPbiI7TzoxNzoiRGF0ZVRpbWVJbW11dGFibGUiOjM6e3M6NDoiZGF0ZSI7czoyNjoiMjAyMC0wMS0wMSAyMDowMDowMC4wMDAwMDAiO3M6MTM6InRpbWV6b25lX3R5cGUiO2k6MTtzOjg6InRpbWV6b25lIjtzOjY6IiswMTowMCI7fXM6MTQ6Im5ld1N0cmVhbVN0YXJ0IjtiOjA7czo4OiJhcmNoaXZlZCI7YjowO319');
+
+ self::assertEquals($event, $message->event());
+ self::assertEquals([
+ 'recordedOn' => new DateTimeImmutable('2020-01-01T20:00:00.000000+0100'),
+ 'newStreamStart' => false,
+ 'archived' => false,
+ ], $message->headers());
+ }
+
+ public function testDeserializeDecodeFailed(): void
+ {
+ $this->expectException(DeserializeFailed::class);
+
+ $eventSerializer = $this->prophesize(EventSerializer::class);
+ $serializer = new EventSerializerMessageSerializer(
+ $eventSerializer->reveal(),
+ );
+
+ $serializer->deserialize('!@#%$^&*()');
+ }
+
+ public function testEquals(): void
+ {
+ $event = new ProfileVisited(
+ ProfileId::fromString('foo'),
+ );
+
+ $message = Message::create($event)
+ ->withRecordedOn(new DateTimeImmutable('2020-01-01T20:00:00.000000+0100'));
+
+ $eventSerializer = $this->prophesize(EventSerializer::class);
+ $eventSerializer->serialize($event)->shouldBeCalledOnce()->willReturn(new SerializedEvent(
+ 'profile_visited',
+ '{id: foo}',
+ ));
+ $eventSerializer->deserialize(new SerializedEvent(
+ 'profile_visited',
+ '{id: foo}',
+ ))->shouldBeCalledOnce()->willReturn($event);
+
+ $serializer = new EventSerializerMessageSerializer(
+ $eventSerializer->reveal(),
+ );
+
+ $content = $serializer->serialize($message);
+ $clonedMessage = $serializer->deserialize($content);
+
+ self::assertEquals($message, $clonedMessage);
+ }
+}