Skip to content

Commit

Permalink
add EventSerializerMessageSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Feb 6, 2024
1 parent 1fffb79 commit 8e562d9
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 5 deletions.
5 changes: 5 additions & 0 deletions baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
<code>Headers</code>
</InvalidReturnType>
</file>
<file src="src/EventBus/Serializer/EventSerializerMessageSerializer.php">
<MixedArgumentTypeCoercion>
<code><![CDATA[$data['headers']]]></code>
</MixedArgumentTypeCoercion>
</file>
<file src="src/Metadata/AggregateRoot/AggregateRootMetadataAwareMetadataFactory.php">
<InvalidReturnStatement>
<code>$aggregate::metadata()</code>
Expand Down
6 changes: 2 additions & 4 deletions src/EventBus/Serializer/DeserializeFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Patchlevel\EventSourcing\EventBus\Serializer;

use Patchlevel\EventSourcing\EventBus\Message;
use RuntimeException;

use function get_debug_type;
Expand All @@ -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),
),
);
Expand Down
71 changes: 71 additions & 0 deletions src/EventBus/Serializer/EventSerializerMessageSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\EventBus\Serializer;

use DateTimeImmutable;
use Patchlevel\EventSourcing\EventBus\Message;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Patchlevel\EventSourcing\Serializer\SerializedEvent;

use function base64_decode;
use function base64_encode;
use function is_array;
use function is_string;
use function serialize;
use function unserialize;

final class EventSerializerMessageSerializer implements MessageSerializer
{
public function __construct(
private readonly EventSerializer $eventSerializer,
) {
}

public function serialize(Message $message): string
{
$serializedEvent = $this->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']);
}
}
2 changes: 1 addition & 1 deletion src/EventBus/Serializer/PhpNativeMessageSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function deserialize(string $content): Message
);

if (!$message instanceof Message) {
throw DeserializeFailed::invalidMessage($message);
throw DeserializeFailed::invalidData($message);
}

return $message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Unit\EventBus\Serializer;

use DateTimeImmutable;
use Patchlevel\EventSourcing\EventBus\Message;
use Patchlevel\EventSourcing\EventBus\Serializer\DeserializeFailed;
use Patchlevel\EventSourcing\EventBus\Serializer\EventSerializerMessageSerializer;
use Patchlevel\EventSourcing\Serializer\EventSerializer;
use Patchlevel\EventSourcing\Serializer\SerializedEvent;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId;
use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;

/** @covers \Patchlevel\EventSourcing\EventBus\Serializer\EventSerializerMessageSerializer */
final class EventSerializerMessageSerializerTest extends TestCase
{
use ProphecyTrait;

public function testSerialize(): 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}',
));

$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);
}
}

0 comments on commit 8e562d9

Please sign in to comment.