From fdd83f419c47755671fc5a685ed02acf038c8b1e Mon Sep 17 00:00:00 2001 From: David Badura Date: Sun, 24 Dec 2023 12:31:11 +0100 Subject: [PATCH] refactor projection errors --- phpcs.xml.dist | 1 + .../Command/ProjectionStatusCommand.php | 6 +-- src/Projection/Projection/Projection.php | 25 +++------ src/Projection/Projection/ProjectionError.php | 21 ++++++++ .../Projection/Store/DoctrineStore.php | 51 +++++++++++-------- .../Projectionist/DefaultProjectionist.php | 5 +- .../Projection/Projection/ProjectionTest.php | 6 +-- .../DefaultProjectionistTest.php | 7 ++- 8 files changed, 72 insertions(+), 50 deletions(-) create mode 100644 src/Projection/Projection/ProjectionError.php diff --git a/phpcs.xml.dist b/phpcs.xml.dist index e09648280..b6748a932 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -13,5 +13,6 @@ + diff --git a/src/Console/Command/ProjectionStatusCommand.php b/src/Console/Command/ProjectionStatusCommand.php index 2746f85ff..e07d7c581 100644 --- a/src/Console/Command/ProjectionStatusCommand.php +++ b/src/Console/Command/ProjectionStatusCommand.php @@ -57,7 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $projection->id()->version(), $projection->position(), $projection->status()->value, - $projection->errorMessage(), + $projection->projectionError()?->errorMessage, ], [...$projections], ), @@ -84,12 +84,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $projection->id()->version(), $projection->position(), $projection->status()->value, - $projection->errorMessage(), + $projection->projectionError()?->errorMessage, ], ], ); - $errorObject = $projection->errorObject(); + $errorObject = $projection->projectionError()?->errorObject; if ($errorObject instanceof Throwable) { $io->throwable($errorObject); diff --git a/src/Projection/Projection/Projection.php b/src/Projection/Projection/Projection.php index 6ac6e4b8a..2180b32b1 100644 --- a/src/Projection/Projection/Projection.php +++ b/src/Projection/Projection/Projection.php @@ -4,16 +4,13 @@ namespace Patchlevel\EventSourcing\Projection\Projection; -use Throwable; - final class Projection { public function __construct( private readonly ProjectionId $id, private ProjectionStatus $status = ProjectionStatus::New, private int $position = 0, - private string|null $errorMessage = null, - private Throwable|null $errorObject = null, + private ProjectionError|null $error = null, ) { } @@ -32,14 +29,9 @@ public function position(): int return $this->position; } - public function errorMessage(): string|null - { - return $this->errorMessage; - } - - public function errorObject(): Throwable|null + public function projectionError(): ProjectionError|null { - return $this->errorObject; + return $this->error; } public function incrementPosition(): void @@ -55,8 +47,7 @@ public function isNew(): bool public function booting(): void { $this->status = ProjectionStatus::Booting; - $this->errorMessage = null; - $this->errorObject = null; + $this->error = null; } public function isBooting(): bool @@ -67,8 +58,7 @@ public function isBooting(): bool public function active(): void { $this->status = ProjectionStatus::Active; - $this->errorMessage = null; - $this->errorObject = null; + $this->error = null; } public function isActive(): bool @@ -86,11 +76,10 @@ public function isOutdated(): bool return $this->status === ProjectionStatus::Outdated; } - public function error(Throwable|string|null $error = null): void + public function error(ProjectionError|null $error = null): void { $this->status = ProjectionStatus::Error; - $this->errorMessage = $error instanceof Throwable ? $error->getMessage() : $error; - $this->errorObject = $error instanceof Throwable ? $error : null; + $this->error = $error; } public function isError(): bool diff --git a/src/Projection/Projection/ProjectionError.php b/src/Projection/Projection/ProjectionError.php new file mode 100644 index 000000000..beba99847 --- /dev/null +++ b/src/Projection/Projection/ProjectionError.php @@ -0,0 +1,21 @@ +getMessage(), $error); + } +} diff --git a/src/Projection/Projection/Store/DoctrineStore.php b/src/Projection/Projection/Store/DoctrineStore.php index 9a01ff879..55ee43a49 100644 --- a/src/Projection/Projection/Store/DoctrineStore.php +++ b/src/Projection/Projection/Store/DoctrineStore.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Types\Types; use Patchlevel\EventSourcing\Projection\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCollection; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionError; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projection\ProjectionNotFound; use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; @@ -16,6 +17,15 @@ use function array_map; +/** @psalm-type Data = array{ + * name: string, + * version: int, + * position: int, + * status: string, + * error_message: string|null, + * error_object: string|null + * } + */ final class DoctrineStore implements ProjectionStore, SchemaConfigurator { public function __construct( @@ -32,7 +42,7 @@ public function get(ProjectionId $projectionId): Projection ->where('name = :name AND version = :version') ->getSQL(); - /** @var array{name: string, version: int, position: int, status: string, error_message: string|null}|false $result */ + /** @var Data|false $result */ $result = $this->connection->fetchAssociative($sql, [ 'name' => $projectionId->name(), 'version' => $projectionId->version(), @@ -42,12 +52,7 @@ public function get(ProjectionId $projectionId): Projection throw new ProjectionNotFound($projectionId); } - return new Projection( - $projectionId, - ProjectionStatus::from($result['status']), - $result['position'], - $result['error_message'], - ); + return $this->createProjection($result); } public function all(): ProjectionCollection @@ -57,31 +62,37 @@ public function all(): ProjectionCollection ->from($this->projectionTable) ->getSQL(); - /** @var list $result */ + /** @var list $result */ $result = $this->connection->fetchAllAssociative($sql); return new ProjectionCollection( array_map( - static function (array $data) { - return new Projection( - new ProjectionId($data['name'], $data['version']), - ProjectionStatus::from($data['status']), - $data['position'], - $data['error_message'], - ErrorSerializer::unserialize($data['error_object']), - ); - }, + fn (array $data) => $this->createProjection($data), $result, ), ); } + /** @param Data $row */ + private function createProjection(array $row): Projection + { + return new Projection( + new ProjectionId($row['name'], $row['version']), + ProjectionStatus::from($row['status']), + $row['position'], + $row['error_message'] ? new ProjectionError( + $row['error_message'], + ErrorSerializer::unserialize($row['error_object']), + ) : null, + ); + } + public function save(Projection ...$projections): void { $this->connection->transactional( function (Connection $connection) use ($projections): void { foreach ($projections as $projection) { - $errorObject = ErrorSerializer::serialize($projection->errorObject()); + $errorObject = ErrorSerializer::serialize($projection->projectionError()?->errorObject); try { $effectedRows = (int)$connection->update( @@ -89,7 +100,7 @@ function (Connection $connection) use ($projections): void { [ 'position' => $projection->position(), 'status' => $projection->status()->value, - 'error_message' => $projection->errorMessage(), + 'error_message' => $projection->projectionError()?->errorMessage, 'error_object' => $errorObject, ], [ @@ -109,7 +120,7 @@ function (Connection $connection) use ($projections): void { 'version' => $projection->id()->version(), 'position' => $projection->position(), 'status' => $projection->status()->value, - 'error_message' => $projection->errorMessage(), + 'error_message' => $projection->projectionError()?->errorMessage, 'error_object' => $errorObject, ], ); diff --git a/src/Projection/Projectionist/DefaultProjectionist.php b/src/Projection/Projectionist/DefaultProjectionist.php index 979bf1367..2bb5e87ef 100644 --- a/src/Projection/Projectionist/DefaultProjectionist.php +++ b/src/Projection/Projectionist/DefaultProjectionist.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\Projection\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCollection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCriteria; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionError; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; use Patchlevel\EventSourcing\Projection\Projection\Store\ProjectionStore; @@ -88,7 +89,7 @@ public function boot( $e->getMessage(), )); - $projection->error($e); + $projection->error(ProjectionError::fromThrowable($e)); $this->projectionStore->save($projection); if ($throwByError) { @@ -400,7 +401,7 @@ private function handleMessage(Message $message, Projection $projection, bool $t ), ); - $projection->error($e); + $projection->error(ProjectionError::fromThrowable($e)); $this->projectionStore->save($projection); if ($throwByError) { diff --git a/tests/Unit/Projection/Projection/ProjectionTest.php b/tests/Unit/Projection/Projection/ProjectionTest.php index 6aab00acf..44468a60b 100644 --- a/tests/Unit/Projection/Projection/ProjectionTest.php +++ b/tests/Unit/Projection/Projection/ProjectionTest.php @@ -5,6 +5,7 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\Projection; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionError; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; use PHPUnit\Framework\TestCase; @@ -68,7 +69,7 @@ public function testError(): void $exception = new RuntimeException('test'); - $projection->error($exception); + $projection->error(ProjectionError::fromThrowable($exception)); self::assertEquals(ProjectionStatus::Error, $projection->status()); self::assertFalse($projection->isNew()); @@ -76,8 +77,7 @@ public function testError(): void self::assertFalse($projection->isActive()); self::assertTrue($projection->isError()); self::assertFalse($projection->isOutdated()); - self::assertEquals('test', $projection->errorMessage()); - self::assertEquals($exception, $projection->errorObject()); + self::assertEquals(new ProjectionError('test', $exception), $projection->projectionError()); } public function testOutdated(): void diff --git a/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php b/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php index 50b0a16f9..6588ff3c7 100644 --- a/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php +++ b/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\Projection\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCollection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCriteria; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionError; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; use Patchlevel\EventSourcing\Projection\Projection\Store\ProjectionStore; @@ -255,8 +256,7 @@ public function create(): void $projector->targetProjection(), ProjectionStatus::Error, 0, - 'ERROR', - $projector->exception, + new ProjectionError('ERROR', $projector->exception), ), ], $projectionStore->savedProjections, @@ -467,8 +467,7 @@ public function handle(Message $message): void $projector->targetProjection(), ProjectionStatus::Error, 0, - 'ERROR', - $projector->exception, + new ProjectionError('ERROR', $projector->exception), ), ], $projectionStore->savedProjections,