Skip to content

Commit 05a0da1

Browse files
committed
Add MissingHeaders, it will be created in the HeaderSerializer, if the Registry cannot find the headers. It is a collection of missing Headers
1 parent 135d68b commit 05a0da1

3 files changed

Lines changed: 91 additions & 4 deletions

File tree

src/Message/MissingHeaders.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Message;
6+
7+
/**
8+
* Collects all headers whose names could not be resolved to a registered class,
9+
* e.g. because the header class was removed. It keeps the raw names and payloads
10+
* so the data is preserved and can be written back without loss.
11+
*/
12+
final class MissingHeaders
13+
{
14+
/** @param array<string, array<string, mixed>> $headers */
15+
public function __construct(
16+
public readonly array $headers,
17+
) {
18+
}
19+
}

src/Message/Serializer/DefaultHeadersSerializer.php

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
namespace Patchlevel\EventSourcing\Message\Serializer;
66

7+
use Patchlevel\EventSourcing\Message\MissingHeaders;
78
use Patchlevel\EventSourcing\Metadata\Message\AttributeMessageHeaderRegistryFactory;
9+
use Patchlevel\EventSourcing\Metadata\Message\HeaderNameNotRegistered;
810
use Patchlevel\EventSourcing\Metadata\Message\MessageHeaderRegistry;
911
use Patchlevel\EventSourcing\Serializer\Encoder\Encoder;
1012
use Patchlevel\EventSourcing\Serializer\Encoder\JsonEncoder;
@@ -30,6 +32,14 @@ public function serialize(array $headers, array $options = []): string
3032
{
3133
$serializedHeaders = [];
3234
foreach ($headers as $header) {
35+
if ($header instanceof MissingHeaders) {
36+
foreach ($header->headers as $name => $payload) {
37+
$serializedHeaders[$name] = $payload;
38+
}
39+
40+
continue;
41+
}
42+
3343
$serializedHeaders[$this->messageHeaderRegistry->headerName($header::class)] = $this->hydrator->extract($header);
3444
}
3545

@@ -46,15 +56,24 @@ public function deserialize(string $string, array $options = []): array
4656
$serializedHeaders = $this->encoder->decode($string, $options);
4757

4858
$headers = [];
59+
$missingHeaders = [];
4960
foreach ($serializedHeaders as $headerName => $headerPayload) {
5061
if (!is_array($headerPayload)) {
5162
throw new InvalidArgument('header payload must be an array');
5263
}
5364

54-
$headers[] = $this->hydrator->hydrate(
55-
$this->messageHeaderRegistry->headerClass($headerName),
56-
$headerPayload,
57-
);
65+
try {
66+
$headers[] = $this->hydrator->hydrate(
67+
$this->messageHeaderRegistry->headerClass($headerName),
68+
$headerPayload,
69+
);
70+
} catch (HeaderNameNotRegistered) {
71+
$missingHeaders[$headerName] = $headerPayload;
72+
}
73+
}
74+
75+
if ($missingHeaders !== []) {
76+
$headers[] = new MissingHeaders($missingHeaders);
5877
}
5978

6079
return $headers;

tests/Unit/Message/Serializer/DefaultHeadersSerializerTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Patchlevel\EventSourcing\Tests\Unit\Message\Serializer;
66

77
use DateTimeImmutable;
8+
use Patchlevel\EventSourcing\Message\MissingHeaders;
89
use Patchlevel\EventSourcing\Message\Serializer\DefaultHeadersSerializer;
910
use Patchlevel\EventSourcing\Metadata\Message\AttributeMessageHeaderRegistryFactory;
1011
use Patchlevel\EventSourcing\Serializer\Encoder\JsonEncoder;
@@ -60,4 +61,52 @@ public function testDeserialize(): void
6061
$deserializedMessage,
6162
);
6263
}
64+
65+
public function testDeserializeUnknownHeadersAsMissingHeaders(): void
66+
{
67+
$serializer = new DefaultHeadersSerializer(
68+
(new AttributeMessageHeaderRegistryFactory())->create([
69+
__DIR__ . '/../../Fixture',
70+
]),
71+
new MetadataHydrator(),
72+
new JsonEncoder(),
73+
);
74+
75+
$deserializedMessage = $serializer->deserialize('{"streamName":{"streamName":"profile-1"},"removed":{"foo":"bar"},"alsoRemoved":{"baz":1}}');
76+
77+
self::assertEquals(
78+
[
79+
new StreamNameHeader('profile-1'),
80+
new MissingHeaders([
81+
'removed' => ['foo' => 'bar'],
82+
'alsoRemoved' => ['baz' => 1],
83+
]),
84+
],
85+
$deserializedMessage,
86+
);
87+
}
88+
89+
public function testSerializeMissingHeadersRoundTrip(): void
90+
{
91+
$serializer = new DefaultHeadersSerializer(
92+
(new AttributeMessageHeaderRegistryFactory())->create([
93+
__DIR__ . '/../../Fixture',
94+
]),
95+
new MetadataHydrator(),
96+
new JsonEncoder(),
97+
);
98+
99+
$content = $serializer->serialize([
100+
new StreamNameHeader('profile-1'),
101+
new MissingHeaders([
102+
'removed' => ['foo' => 'bar'],
103+
'alsoRemoved' => ['baz' => 1],
104+
]),
105+
]);
106+
107+
self::assertEquals(
108+
'{"streamName":{"streamName":"profile-1"},"removed":{"foo":"bar"},"alsoRemoved":{"baz":1}}',
109+
$content,
110+
);
111+
}
63112
}

0 commit comments

Comments
 (0)