Skip to content

Commit

Permalink
Major overhaul ...
Browse files Browse the repository at this point in the history
  • Loading branch information
bwaidelich committed Dec 10, 2024
1 parent 1f5eac0 commit 4b895cf
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 353 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
php-versions: [ '8.1', '8.2', '8.3' ]
php-versions: [ '8.4' ]

runs-on: ubuntu-latest

Expand Down
70 changes: 25 additions & 45 deletions src/Helpers/InMemoryEventStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
namespace Wwwision\DCBEventStore\Helpers;

use DateTimeImmutable;
use RuntimeException;
use Wwwision\DCBEventStore\EventStore;
use Wwwision\DCBEventStore\EventStream;
use Wwwision\DCBEventStore\Exceptions\ConditionalAppendFailed;
use Wwwision\DCBEventStore\Types\AppendCondition;
use Wwwision\DCBEventStore\Types\Event;
Expand All @@ -16,10 +14,8 @@
use Wwwision\DCBEventStore\Types\Events;
use Wwwision\DCBEventStore\Types\ReadOptions;
use Wwwision\DCBEventStore\Types\SequenceNumber;
use Wwwision\DCBEventStore\Types\StreamQuery\Criteria\EventTypesAndTagsCriterion;
use Wwwision\DCBEventStore\Types\StreamQuery\Criterion;
use Wwwision\DCBEventStore\Types\StreamQuery\CriterionHashes;
use Wwwision\DCBEventStore\Types\StreamQuery\StreamQuery;

use function count;

/**
Expand Down Expand Up @@ -49,56 +45,46 @@ public static function create(): self

public function read(StreamQuery $query, ?ReadOptions $options = null): InMemoryEventStream
{
$matchingCriterionHashesBySequenceNumber = [];
$eventEnvelopes = $this->eventEnvelopes;
foreach ($query->criteria as $criterion) {
$onlyLastEvent = $criterion instanceof EventTypesAndTagsCriterion && $criterion->onlyLastEvent;
if ($onlyLastEvent) {
$eventEnvelopes = EventEnvelopes::fromArray(array_reverse(iterator_to_array($eventEnvelopes)));
}
foreach ($eventEnvelopes as $eventEnvelope) {
if (!self::criterionMatchesEvent($criterion, $eventEnvelope->event)) {
continue;
}
$sequenceNumber = $eventEnvelope->sequenceNumber->value;
if (!array_key_exists($sequenceNumber, $matchingCriterionHashesBySequenceNumber)) {
$matchingCriterionHashesBySequenceNumber[$sequenceNumber] = [];
$options ??= ReadOptions::create();

if ($query->isWildcard()) {
$eventEnvelopes = $this->eventEnvelopes;
} else {
/** @var array<int,EventEnvelope> $matchingEventEnvelopesBySequenceNumber */
$matchingEventEnvelopesBySequenceNumber = [];
$eventEnvelopes = $this->eventEnvelopes;
foreach ($query->criteria as $criterion) {
if ($criterion->onlyLastEvent) {
$eventEnvelopes = EventEnvelopes::fromArray(array_reverse(iterator_to_array($eventEnvelopes)));
}
$matchingCriterionHashesBySequenceNumber[$sequenceNumber][] = $criterion->hash();
if ($onlyLastEvent) {
continue 2;
foreach ($eventEnvelopes as $eventEnvelope) {
$sequenceNumber = $eventEnvelope->sequenceNumber->value;
if (!$criterion->matchesEvent($eventEnvelope->event)) {
continue;
}
$matchingEventEnvelopesBySequenceNumber[$sequenceNumber] = $eventEnvelope;
if ($criterion->onlyLastEvent) {
continue 2;
}
}
}
ksort($matchingEventEnvelopesBySequenceNumber, SORT_NUMERIC);
$eventEnvelopes = array_values($matchingEventEnvelopesBySequenceNumber);
}

$matchingEventEnvelopes = [];
$eventEnvelopes = $this->eventEnvelopes;
$options ??= ReadOptions::create();
if ($options->backwards) {
$eventEnvelopes = EventEnvelopes::fromArray(array_reverse(iterator_to_array($eventEnvelopes)));
}
$matchingEventEnvelopes = [];
foreach ($eventEnvelopes as $eventEnvelope) {
$sequenceNumber = $eventEnvelope->sequenceNumber->value;
if ($options->from !== null && (($options->backwards && $sequenceNumber > $options->from->value) || (!$options->backwards && $sequenceNumber < $options->from->value))) {
continue;
}
if (!array_key_exists($sequenceNumber, $matchingCriterionHashesBySequenceNumber) && !$query->isWildcard()) {
continue;
}

$matchingEventEnvelopes[] = $eventEnvelope->withCriterionHashes(CriterionHashes::fromArray($matchingCriterionHashesBySequenceNumber[$sequenceNumber] ?? []));
$matchingEventEnvelopes[] = $eventEnvelope;
}
return InMemoryEventStream::create(...$matchingEventEnvelopes);
}

private static function criterionMatchesEvent(Criterion $criterion, Event $event): bool
{
return match ($criterion::class) {
EventTypesAndTagsCriterion::class => ($criterion->tags === null || $event->tags->containEvery($criterion->tags)) && ($criterion->eventTypes === null || $criterion->eventTypes->contain($event->type)),
default => throw new RuntimeException(sprintf('The criterion type "%s" is not supported by the %s', $criterion::class, self::class), 1700302540),
};
}

public function append(Events|Event $events, AppendCondition $condition): void
{
if (!$condition->expectedHighestSequenceNumber->isAny()) {
Expand All @@ -123,17 +109,11 @@ public function append(Events|Event $events, AppendCondition $condition): void
new EventEnvelope(
$sequenceNumber,
new DateTimeImmutable(),
CriterionHashes::none(),
$event,
)
);
$sequenceNumber = $sequenceNumber->next();
}
$this->eventEnvelopes = $this->eventEnvelopes->append($newEventEnvelopes);
}

public function resetState(): void
{
$this->eventEnvelopes = EventEnvelopes::none();
}
}
1 change: 0 additions & 1 deletion src/Helpers/SystemClock.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use DateTimeImmutable;
use Psr\Clock\ClockInterface;
use Wwwision\DCBEventStore\EventStream;

final class SystemClock implements ClockInterface
{
Expand Down
7 changes: 0 additions & 7 deletions src/Types/EventEnvelope.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Wwwision\DCBEventStore\Types;

use DateTimeImmutable;
use Wwwision\DCBEventStore\Types\StreamQuery\CriterionHashes;

/**
* An {@see Event} with its global {@see SequenceNumber} in the Events Store
Expand All @@ -15,13 +14,7 @@ final class EventEnvelope
public function __construct(
public readonly SequenceNumber $sequenceNumber,
public readonly DateTimeImmutable $recordedAt,
public readonly CriterionHashes $criterionHashes,
public readonly Event $event,
) {
}

public function withCriterionHashes(CriterionHashes $criterionHashes): self
{
return new self($this->sequenceNumber, $this->recordedAt, $criterionHashes, $this->event);
}
}
2 changes: 1 addition & 1 deletion src/Types/EventType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Webmozart\Assert\Assert;

/**
* The type of an event, e.g. "CustomerRenamed"
* The type of event, e.g. "CustomerRenamed"
*/
final class EventType implements JsonSerializable
{
Expand Down
14 changes: 10 additions & 4 deletions src/Types/ReadOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ private function __construct(
}

public static function create(
?SequenceNumber $from = null,
?bool $backwards = null,
SequenceNumber|int|null $from = null,
bool|null $backwards = null,
): self {
if (is_int($from)) {
$from = SequenceNumber::fromInteger($from);
}
return new self(
$from,
$backwards ?? false,
);
}

public function with(
?SequenceNumber $from = null,
?bool $backwards = null,
SequenceNumber|int|null $from = null,
bool|null $backwards = null,
): self {
if (is_int($from)) {
$from = SequenceNumber::fromInteger($from);
}
return new self(
$from ?? $this->from,
$backwards ?? $this->backwards,
Expand Down
38 changes: 24 additions & 14 deletions src/Types/StreamQuery/Criteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,42 @@
use IteratorAggregate;
use JsonSerializable;
use Traversable;
use Wwwision\DCBEventStore\Types\Event;
use Wwwision\DCBEventStore\Types\StreamQuery\Criteria\EventTypesAndTagsCriterion;

use function array_map;

/**
* A type-safe set of {@see Criterion} instances
* A type-safe set of {@see EventTypesAndTagsCriterion} instances
*
* @implements IteratorAggregate<Criterion>
* @implements IteratorAggregate<EventTypesAndTagsCriterion>
*/
final class Criteria implements IteratorAggregate, JsonSerializable
{
/**
* @var Criterion[]
* @var EventTypesAndTagsCriterion[]
*/
private readonly array $criteria;

private function __construct(Criterion ...$criteria)
private function __construct(EventTypesAndTagsCriterion ...$criteria)
{
$this->criteria = $criteria;
}

/**
* @param Criterion[] $criteria
* @param EventTypesAndTagsCriterion[] $criteria
*/
public static function fromArray(array $criteria): self
{
return new self(...$criteria);
}

public static function create(Criterion ...$criteria): self
public static function create(EventTypesAndTagsCriterion ...$criteria): self
{
return new self(...$criteria);
}

public function with(Criterion $criterion): self
public function with(EventTypesAndTagsCriterion $criterion): self
{
return new self(...[...$this->criteria, $criterion]);
}
Expand All @@ -58,27 +60,35 @@ public function getIterator(): Traversable
return new ArrayIterator($this->criteria);
}

public function matchesEvent(Event $event): bool
{
if ($this->isEmpty()) {
return true;
}
foreach ($this->criteria as $criterion) {
if ($criterion->matchesEvent($event)) {
return true;
}
}
return false;
}

public function isEmpty(): bool
{
return $this->criteria === [];
}

/**
* @param Closure(Criterion $criterion): mixed $callback
* @param Closure(EventTypesAndTagsCriterion $criterion): mixed $callback
* @return array<string, mixed>
*/
public function map(Closure $callback): array
{
return array_map($callback, $this->criteria);
}

public function hashes(): CriterionHashes
{
return CriterionHashes::fromArray(array_map(static fn (Criterion $criterion) => $criterion->hash(), $this->criteria));
}

/**
* @return Criterion[]
* @return EventTypesAndTagsCriterion[]
*/
public function jsonSerialize(): array
{
Expand Down
23 changes: 10 additions & 13 deletions src/Types/StreamQuery/Criteria/EventTypesAndTagsCriterion.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,19 @@
namespace Wwwision\DCBEventStore\Types\StreamQuery\Criteria;

use InvalidArgumentException;
use Wwwision\DCBEventStore\Types\Event;
use Wwwision\DCBEventStore\Types\EventType;
use Wwwision\DCBEventStore\Types\EventTypes;
use Wwwision\DCBEventStore\Types\StreamQuery\Criterion;
use Wwwision\DCBEventStore\Types\StreamQuery\CriterionHash;
use Wwwision\DCBEventStore\Types\Tag;
use Wwwision\DCBEventStore\Types\Tags;

final class EventTypesAndTagsCriterion implements Criterion
final class EventTypesAndTagsCriterion
{
private readonly CriterionHash $hash;

private function __construct(
public readonly EventTypes|null $eventTypes,
public readonly Tags|null $tags,
public readonly bool $onlyLastEvent,
) {
$this->hash = CriterionHash::fromParts(
substr(substr(self::class, 0, -9), strrpos(self::class, '\\') + 1),
implode(',', $eventTypes?->toStringArray() ?? []),
implode(',', $tags?->toStrings() ?? []),
$onlyLastEvent ? 'onlyLastEvent' : '',
);
}

/**
Expand Down Expand Up @@ -80,8 +71,14 @@ public function with(
);
}

public function hash(): CriterionHash
public function matchesEvent(Event $event): bool
{
return $this->hash;
if ($this->tags !== null && !$event->tags->containEvery($this->tags)) {
return false;
}
if ($this->eventTypes !== null && !$this->eventTypes->contain($event->type)) {
return false;
}
return true;
}
}
15 changes: 0 additions & 15 deletions src/Types/StreamQuery/Criterion.php

This file was deleted.

39 changes: 0 additions & 39 deletions src/Types/StreamQuery/CriterionHash.php

This file was deleted.

Loading

0 comments on commit 4b895cf

Please sign in to comment.