Skip to content

Commit 29f6737

Browse files
authored
Merge pull request #785 from patchlevel/update-inmemory-store
Add support for EventsCritereon and ToIndexCriteroen in InMemoryStore, Fix FromIndexCriteroen behaviour and add Index-, EventId-, RecordedHeader
2 parents a75ef9c + efb2da8 commit 29f6737

File tree

7 files changed

+509
-73
lines changed

7 files changed

+509
-73
lines changed

baseline.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,6 @@
159159
<code><![CDATA[$dateTimeType->convertToPHPValue($data['recorded_on'], $platform)]]></code>
160160
</MixedArgument>
161161
</file>
162-
<file src="src/Store/InMemoryStore.php">
163-
<MixedPropertyTypeCoercion>
164-
<code><![CDATA[$this->messages]]></code>
165-
</MixedPropertyTypeCoercion>
166-
</file>
167162
<file src="src/Store/StreamDoctrineDbalStore.php">
168163
<DeprecatedMethod>
169164
<code><![CDATA[setPrimaryKey]]></code>

deptrac-baseline.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,12 @@ deptrac:
1414
- Patchlevel\EventSourcing\Aggregate\AggregateRoot
1515
Patchlevel\EventSourcing\Attribute\Subscriber:
1616
- Patchlevel\EventSourcing\Subscription\RunMode
17+
Patchlevel\EventSourcing\Store\DoctrineDbalStore:
18+
- Patchlevel\EventSourcing\Aggregate\AggregateHeader
19+
Patchlevel\EventSourcing\Store\DoctrineDbalStoreStream:
20+
- Patchlevel\EventSourcing\Aggregate\AggregateHeader
21+
Patchlevel\EventSourcing\Store\InMemoryStore:
22+
- Patchlevel\EventSourcing\Aggregate\AggregateHeader
23+
- Patchlevel\EventSourcing\Metadata\Event\EventRegistry
24+
Patchlevel\EventSourcing\Store\MissingEventRegistry:
25+
- Patchlevel\EventSourcing\Metadata\Event\EventRegistry

deptrac.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ deptrac:
186186
- Cryptography
187187
- MetadataAggregate
188188
Store:
189-
- Aggregate
190-
- Attribute
191189
- Clock
192190
- Message
193191
- Metadata

src/Store/Header/IndexHeader.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44

55
namespace Patchlevel\EventSourcing\Store\Header;
66

7-
/**
8-
* @psalm-immutable
9-
* @experimental
10-
*/
7+
/** @psalm-immutable */
118
final class IndexHeader
129
{
1310
/** @param positive-int $index */

src/Store/InMemoryStore.php

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,30 @@
66

77
use Closure;
88
use Patchlevel\EventSourcing\Aggregate\AggregateHeader;
9+
use Patchlevel\EventSourcing\Clock\SystemClock;
910
use Patchlevel\EventSourcing\Message\HeaderNotFound;
1011
use Patchlevel\EventSourcing\Message\Message;
12+
use Patchlevel\EventSourcing\Metadata\Event\EventRegistry;
1113
use Patchlevel\EventSourcing\Store\Criteria\AggregateIdCriterion;
1214
use Patchlevel\EventSourcing\Store\Criteria\AggregateNameCriterion;
1315
use Patchlevel\EventSourcing\Store\Criteria\ArchivedCriterion;
1416
use Patchlevel\EventSourcing\Store\Criteria\Criteria;
17+
use Patchlevel\EventSourcing\Store\Criteria\EventsCriterion;
1518
use Patchlevel\EventSourcing\Store\Criteria\FromIndexCriterion;
1619
use Patchlevel\EventSourcing\Store\Criteria\FromPlayheadCriterion;
1720
use Patchlevel\EventSourcing\Store\Criteria\StreamCriterion;
21+
use Patchlevel\EventSourcing\Store\Criteria\ToIndexCriterion;
22+
use Patchlevel\EventSourcing\Store\Header\EventIdHeader;
23+
use Patchlevel\EventSourcing\Store\Header\IndexHeader;
1824
use Patchlevel\EventSourcing\Store\Header\PlayheadHeader;
25+
use Patchlevel\EventSourcing\Store\Header\RecordedOnHeader;
1926
use Patchlevel\EventSourcing\Store\Header\StreamNameHeader;
27+
use Psr\Clock\ClockInterface;
28+
use Ramsey\Uuid\Uuid;
29+
use Throwable;
2030

2131
use function array_filter;
2232
use function array_map;
23-
use function array_push;
2433
use function array_reverse;
2534
use function array_slice;
2635
use function array_unique;
@@ -35,10 +44,16 @@
3544

3645
final class InMemoryStore implements StreamStore
3746
{
38-
/** @param array<positive-int|0, Message> $messages */
47+
/** @var array<0|positive-int, Message> */
48+
private array $messages = [];
49+
50+
/** @param list<Message> $messages */
3951
public function __construct(
40-
private array $messages = [],
52+
array $messages = [],
53+
private readonly EventRegistry|null $eventRegistry = null,
54+
private readonly ClockInterface $clock = new SystemClock(),
4155
) {
56+
$this->save(...$messages);
4257
}
4358

4459
public function load(
@@ -71,7 +86,27 @@ public function count(Criteria|null $criteria = null): int
7186

7287
public function save(Message ...$messages): void
7388
{
74-
array_push($this->messages, ...$messages);
89+
$this->transactional(function () use ($messages): void {
90+
$count = count($this->messages);
91+
92+
foreach ($messages as $message) {
93+
$count++;
94+
95+
if (!$message->hasHeader(IndexHeader::class)) {
96+
$message = $message->withHeader(new IndexHeader($count));
97+
}
98+
99+
if (!$message->hasHeader(EventIdHeader::class)) {
100+
$message = $message->withHeader(new EventIdHeader(Uuid::uuid7()->toString()));
101+
}
102+
103+
if (!$message->hasHeader(RecordedOnHeader::class)) {
104+
$message = $message->withHeader(new RecordedOnHeader($this->clock->now()));
105+
}
106+
107+
$this->messages[] = $message;
108+
}
109+
});
75110
}
76111

77112
/**
@@ -81,7 +116,14 @@ public function save(Message ...$messages): void
81116
*/
82117
public function transactional(Closure $function): void
83118
{
84-
$function();
119+
$messages = $this->messages;
120+
try {
121+
$function();
122+
} catch (Throwable $e) {
123+
$this->messages = $messages;
124+
125+
throw $e;
126+
}
85127
}
86128

87129
/** @return list<string> */
@@ -134,9 +176,11 @@ private function filter(Criteria|null $criteria): array
134176
return $this->messages;
135177
}
136178

179+
$eventRegistry = $this->eventRegistry;
180+
137181
return array_filter(
138182
$this->messages,
139-
static function (Message $message, int $index) use ($criteria): bool {
183+
static function (Message $message) use ($criteria, $eventRegistry): bool {
140184
foreach ($criteria->all() as $criterion) {
141185
switch ($criterion::class) {
142186
case AggregateIdCriterion::class:
@@ -222,7 +266,35 @@ static function (Message $message, int $index) use ($criteria): bool {
222266

223267
break;
224268
case FromIndexCriterion::class:
225-
if ($index < $criterion->fromIndex) {
269+
try {
270+
$index = $message->header(IndexHeader::class)->index;
271+
} catch (HeaderNotFound) {
272+
return false;
273+
}
274+
275+
if ($index <= $criterion->fromIndex) {
276+
return false;
277+
}
278+
279+
break;
280+
case ToIndexCriterion::class:
281+
try {
282+
$index = $message->header(IndexHeader::class)->index;
283+
} catch (HeaderNotFound) {
284+
return false;
285+
}
286+
287+
if ($index >= $criterion->toIndex) {
288+
return false;
289+
}
290+
291+
break;
292+
case EventsCriterion::class:
293+
if ($eventRegistry === null) {
294+
throw new MissingEventRegistry($criterion::class);
295+
}
296+
297+
if (!in_array($eventRegistry->eventName($message->event()::class), $criterion->events)) {
226298
return false;
227299
}
228300

src/Store/MissingEventRegistry.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\Store;
6+
7+
use Patchlevel\EventSourcing\Metadata\Event\EventRegistry;
8+
use RuntimeException;
9+
10+
use function sprintf;
11+
12+
final class MissingEventRegistry extends RuntimeException
13+
{
14+
/** @param class-string $criterionClass */
15+
public function __construct(string $criterionClass)
16+
{
17+
parent::__construct(sprintf('criterion %s not supported without an %s given', $criterionClass, EventRegistry::class));
18+
}
19+
}

0 commit comments

Comments
 (0)