From b7d3e1db8aaf35988a16d9c43df7588007534fb9 Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 5 Jan 2024 15:17:51 +0100 Subject: [PATCH] transfrom projector throwable into an array --- baseline.xml | 23 +++++----- phpstan-baseline.neon | 5 ++ .../Command/ProjectionStatusCommand.php | 19 ++++++-- src/Projection/Projection/ProjectionError.php | 7 ++- .../Projection/Store/DoctrineStore.php | 23 ++++++---- .../Projection/Store/ErrorContext.php | 36 +++++++++++++++ .../Projection/Store/ErrorSerializer.php | 41 ----------------- .../Projection/Projection/ProjectionTest.php | 9 +++- .../Projection/Store/ErrorContextTest.php | 41 +++++++++++++++++ .../Projection/Store/ErrorSerializerTest.php | 46 ------------------- .../DefaultProjectionistTest.php | 5 +- 11 files changed, 140 insertions(+), 115 deletions(-) create mode 100644 src/Projection/Projection/Store/ErrorContext.php delete mode 100644 src/Projection/Projection/Store/ErrorSerializer.php create mode 100644 tests/Unit/Projection/Projection/Store/ErrorContextTest.php delete mode 100644 tests/Unit/Projection/Projection/Store/ErrorSerializerTest.php diff --git a/baseline.xml b/baseline.xml index 552bc8ad..0dd1d987 100644 --- a/baseline.xml +++ b/baseline.xml @@ -50,6 +50,18 @@ ]]> + + + $context + + + $context + + + errorContext]]> + errorContext]]> + + projectors]]> @@ -135,12 +147,6 @@ $name - - - toString - toString - - $id @@ -153,11 +159,6 @@ $name - - - toString - - $id diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b5432015..6262131e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -5,6 +5,11 @@ parameters: count: 1 path: src/EventBus/Message.php + - + message: "#^Parameter \\#2 \\$errorContext of class Patchlevel\\\\EventSourcing\\\\Projection\\\\Projection\\\\ProjectionError constructor expects array\\\\|null, mixed given\\.$#" + count: 1 + path: src/Projection/Projection/Store/DoctrineStore.php + - message: "#^Method Patchlevel\\\\EventSourcing\\\\Projection\\\\Projector\\\\InMemoryProjectorRepository\\:\\:projectors\\(\\) should return array\\ but returns array\\\\.$#" count: 1 diff --git a/src/Console/Command/ProjectionStatusCommand.php b/src/Console/Command/ProjectionStatusCommand.php index e07d7c58..4f2796e1 100644 --- a/src/Console/Command/ProjectionStatusCommand.php +++ b/src/Console/Command/ProjectionStatusCommand.php @@ -8,14 +8,16 @@ use Patchlevel\EventSourcing\Console\OutputStyle; use Patchlevel\EventSourcing\Projection\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; +use Patchlevel\EventSourcing\Projection\Projection\Store\ErrorContext; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Throwable; use function array_map; +use function is_array; +/** @psalm-import-type Context from ErrorContext */ #[AsCommand( 'event-sourcing:projection:status', 'View the current status of the projections', @@ -89,12 +91,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int ], ); - $errorObject = $projection->projectionError()?->errorObject; + $contexts = $projection->projectionError()?->errorContext; - if ($errorObject instanceof Throwable) { - $io->throwable($errorObject); + if (is_array($contexts)) { + foreach ($contexts as $context) { + $this->displayError($io, $context); + } } return 0; } + + /** @param Context $context */ + private function displayError(OutputStyle $io, array $context): void + { + $io->error($context['message']); + $io->block($context['trace']); + } } diff --git a/src/Projection/Projection/ProjectionError.php b/src/Projection/Projection/ProjectionError.php index beba9984..1500b768 100644 --- a/src/Projection/Projection/ProjectionError.php +++ b/src/Projection/Projection/ProjectionError.php @@ -4,18 +4,21 @@ namespace Patchlevel\EventSourcing\Projection\Projection; +use Patchlevel\EventSourcing\Projection\Projection\Store\ErrorContext; use Throwable; +/** @psalm-import-type Context from ErrorContext */ final class ProjectionError { + /** @param list|null $errorContext */ public function __construct( public readonly string $errorMessage, - public readonly Throwable|null $errorObject = null, + public readonly array|null $errorContext = null, ) { } public static function fromThrowable(Throwable $error): self { - return new self($error->getMessage(), $error); + return new self($error->getMessage(), ErrorContext::fromThrowable($error)); } } diff --git a/src/Projection/Projection/Store/DoctrineStore.php b/src/Projection/Projection/Store/DoctrineStore.php index ff8ebb61..fd2e6c00 100644 --- a/src/Projection/Projection/Store/DoctrineStore.php +++ b/src/Projection/Projection/Store/DoctrineStore.php @@ -16,6 +16,10 @@ use Patchlevel\EventSourcing\Schema\SchemaConfigurator; use function array_map; +use function json_decode; +use function json_encode; + +use const JSON_THROW_ON_ERROR; /** @psalm-type Data = array{ * name: string, @@ -23,7 +27,7 @@ * position: int, * status: string, * error_message: string|null, - * error_object: string|null, + * error_context: string|null, * retry: int, * } */ @@ -77,13 +81,16 @@ public function all(): ProjectionCollection /** @param Data $row */ private function createProjection(array $row): Projection { + $context = $row['error_context'] ? + json_decode($row['error_context'], true, 512, JSON_THROW_ON_ERROR) : null; + 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']), + $context, ) : null, $row['retry'], ); @@ -94,7 +101,7 @@ public function save(Projection ...$projections): void $this->connection->transactional( function (Connection $connection) use ($projections): void { foreach ($projections as $projection) { - $errorObject = ErrorSerializer::serialize($projection->projectionError()?->errorObject); + $projectionError = $projection->projectionError(); try { $effectedRows = (int)$connection->update( @@ -102,8 +109,8 @@ function (Connection $connection) use ($projections): void { [ 'position' => $projection->position(), 'status' => $projection->status()->value, - 'error_message' => $projection->projectionError()?->errorMessage, - 'error_object' => $errorObject, + 'error_message' => $projectionError?->errorMessage, + 'error_context' => $projectionError?->errorContext ? json_encode($projectionError->errorContext, JSON_THROW_ON_ERROR) : null, 'retry' => $projection->retry(), ], [ @@ -123,8 +130,8 @@ function (Connection $connection) use ($projections): void { 'version' => $projection->id()->version(), 'position' => $projection->position(), 'status' => $projection->status()->value, - 'error_message' => $projection->projectionError()?->errorMessage, - 'error_object' => $errorObject, + 'error_message' => $projectionError?->errorMessage, + 'error_context' => $projectionError?->errorContext ? json_encode($projectionError->errorContext, JSON_THROW_ON_ERROR) : null, 'retry' => $projection->retry(), ], ); @@ -162,7 +169,7 @@ public function configureSchema(Schema $schema, Connection $connection): void ->setNotnull(true); $table->addColumn('error_message', Types::STRING) ->setNotnull(false); - $table->addColumn('error_object', Types::BLOB) + $table->addColumn('error_context', Types::JSON) ->setNotnull(false); $table->addColumn('retry', Types::INTEGER) ->setNotnull(true) diff --git a/src/Projection/Projection/Store/ErrorContext.php b/src/Projection/Projection/Store/ErrorContext.php new file mode 100644 index 00000000..af18b365 --- /dev/null +++ b/src/Projection/Projection/Store/ErrorContext.php @@ -0,0 +1,36 @@ + */ + public static function fromThrowable(Throwable $error): array + { + $errors = []; + + do { + $errors[] = self::transform($error); + $error = $error->getPrevious(); + } while ($error); + + return $errors; + } + + /** @return Context */ + private static function transform(Throwable $error): array + { + return [ + 'message' => $error->getMessage(), + 'code' => $error->getCode(), + 'file' => $error->getFile(), + 'line' => $error->getLine(), + 'trace' => $error->getTraceAsString(), + ]; + } +} diff --git a/src/Projection/Projection/Store/ErrorSerializer.php b/src/Projection/Projection/Store/ErrorSerializer.php deleted file mode 100644 index 523af671..00000000 --- a/src/Projection/Projection/Store/ErrorSerializer.php +++ /dev/null @@ -1,41 +0,0 @@ - true]); - - if (!$result instanceof Throwable) { - return null; - } - - return $result; - } -} diff --git a/tests/Unit/Projection/Projection/ProjectionTest.php b/tests/Unit/Projection/Projection/ProjectionTest.php index 44468a60..620dd237 100644 --- a/tests/Unit/Projection/Projection/ProjectionTest.php +++ b/tests/Unit/Projection/Projection/ProjectionTest.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\Projection\Projection\ProjectionError; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; +use Patchlevel\EventSourcing\Projection\Projection\Store\ErrorContext; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -77,7 +78,13 @@ public function testError(): void self::assertFalse($projection->isActive()); self::assertTrue($projection->isError()); self::assertFalse($projection->isOutdated()); - self::assertEquals(new ProjectionError('test', $exception), $projection->projectionError()); + self::assertEquals( + new ProjectionError( + 'test', + ErrorContext::fromThrowable($exception), + ), + $projection->projectionError(), + ); } public function testOutdated(): void diff --git a/tests/Unit/Projection/Projection/Store/ErrorContextTest.php b/tests/Unit/Projection/Projection/Store/ErrorContextTest.php new file mode 100644 index 00000000..2006e896 --- /dev/null +++ b/tests/Unit/Projection/Projection/Store/ErrorContextTest.php @@ -0,0 +1,41 @@ +exception), + new ProjectionError('ERROR', ErrorContext::fromThrowable($projector->exception)), -1, ), ], @@ -463,7 +464,7 @@ public function handle(Message $message): void $projectionId, ProjectionStatus::Error, 0, - new ProjectionError('ERROR', $projector->exception), + new ProjectionError('ERROR', ErrorContext::fromThrowable($projector->exception)), 1, ), ],