diff --git a/docs/pages/getting_started.md b/docs/pages/getting_started.md index ca32cfb02..9b8c53ea1 100644 --- a/docs/pages/getting_started.md +++ b/docs/pages/getting_started.md @@ -281,6 +281,7 @@ use Doctrine\DBAL\DriverManager; use Patchlevel\EventSourcing\EventBus\DefaultEventBus; use Patchlevel\EventSourcing\Projection\Projection\Store\DoctrineStore; use Patchlevel\EventSourcing\Projection\Projectionist\DefaultProjectionist; +use Patchlevel\EventSourcing\Projection\Projector\MetadataProjectorAccessorRepository; use Patchlevel\EventSourcing\Repository\DefaultRepositoryManager; use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer; use Patchlevel\EventSourcing\Store\DoctrineDbalStore; @@ -306,7 +307,7 @@ $eventStore = new DoctrineDbalStore( $hotelProjector = new HotelProjector($projectionConnection); -$projectorRepository = new ProjectorRepository([ +$projectorRepository = new MetadataProjectorAccessorRepository([ $hotelProjector, ]); diff --git a/docs/pages/projection.md b/docs/pages/projection.md index ecfe7916f..a57b3246d 100644 --- a/docs/pages/projection.md +++ b/docs/pages/projection.md @@ -469,6 +469,17 @@ $retryStrategy = new ClockBasedRetryStrategy( You can reactivate the projection manually or remove it and rebuild it from scratch. +### Projector Accessor + +The projector accessor is responsible for providing the projectors to the projectionist. +We provide a metadata projector accessor repository by default. + +```php +use Patchlevel\EventSourcing\Projection\Projector\MetadataProjectorAccessorRepository; + +$projectorAccessorRepository = new MetadataProjectorAccessorRepository([$projector1, $projector2, $projector3]); +``` + ### Projectionist Now we can create the projectionist and plug together the necessary services. @@ -481,7 +492,7 @@ use Patchlevel\EventSourcing\Projection\Projectionist\DefaultProjectionist; $projectionist = new DefaultProjectionist( $eventStore, $projectionStore, - [$projector1, $projector2, $projector3], + $projectorAccessorRepository, $retryStrategy, ); ``` diff --git a/tests/Unit/Projection/Projector/MetadataProjectorAccessorRepositoryTest.php b/tests/Unit/Projection/Projector/MetadataProjectorAccessorRepositoryTest.php new file mode 100644 index 000000000..48615474d --- /dev/null +++ b/tests/Unit/Projection/Projector/MetadataProjectorAccessorRepositoryTest.php @@ -0,0 +1,45 @@ +all()); + self::assertNull($repository->get('foo')); + } + + public function testWithProjector(): void + { + $projector = new #[Projector('foo')] + class { + }; + $metadataFactory = new AttributeProjectorMetadataFactory(); + + $repository = new MetadataProjectorAccessorRepository( + [$projector], + $metadataFactory, + ); + + $accessor = new MetadataProjectorAccessor( + $projector, + $metadataFactory->metadata($projector::class), + ); + + self::assertEquals([$accessor], $repository->all()); + self::assertEquals($accessor, $repository->get('foo')); + } +} diff --git a/tests/Unit/Projection/Projector/MetadataProjectorAccessorTest.php b/tests/Unit/Projection/Projector/MetadataProjectorAccessorTest.php new file mode 100644 index 000000000..9b5d801e4 --- /dev/null +++ b/tests/Unit/Projection/Projector/MetadataProjectorAccessorTest.php @@ -0,0 +1,206 @@ +metadata($projector::class), + ); + + self::assertEquals('profile', $accessor->id()); + } + + public function testGroup(): void + { + $projector = new #[Projector('profile')] + class { + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + self::assertEquals('default', $accessor->group()); + } + + public function testRunMode(): void + { + $projector = new #[Projector('profile')] + class { + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + self::assertEquals(RunMode::FromBeginning, $accessor->runMode()); + } + + public function testSubscribeMethod(): void + { + $projector = new #[Projector('profile')] + class { + #[Subscribe(ProfileCreated::class)] + public function onProfileCreated(Message $message): void + { + } + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->subscribeMethods(ProfileCreated::class); + + self::assertEquals([ + $projector->onProfileCreated(...), + ], $result); + } + + public function testMultipleSubscribeMethod(): void + { + $projector = new #[Projector('profile')] + class { + #[Subscribe(ProfileCreated::class)] + public function onProfileCreated(Message $message): void + { + } + + #[Subscribe(ProfileCreated::class)] + public function onFoo(Message $message): void + { + } + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->subscribeMethods(ProfileCreated::class); + + self::assertEquals([ + $projector->onProfileCreated(...), + $projector->onFoo(...), + ], $result); + } + + public function testSubscribeAllMethod(): void + { + $projector = new #[Projector('profile')] + class { + #[Subscribe('*')] + public function onProfileCreated(Message $message): void + { + } + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->subscribeMethods(ProfileCreated::class); + + self::assertEquals([ + $projector->onProfileCreated(...), + ], $result); + } + + public function testSetupMethod(): void + { + $projector = new #[Projector('profile')] + class { + #[Setup] + public function method(): void + { + } + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->setupMethod(); + + self::assertEquals($projector->method(...), $result); + } + + public function testNotSetupMethod(): void + { + $projector = new #[Projector('profile')] + class { + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->setupMethod(); + + self::assertNull($result); + } + + public function testTeardownMethod(): void + { + $projector = new #[Projector('profile')] + class { + #[Teardown] + public function method(): void + { + } + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->teardownMethod(); + + self::assertEquals($projector->method(...), $result); + } + + public function testNotTeardownMethod(): void + { + $projector = new #[Projector('profile')] + class { + }; + + $accessor = new MetadataProjectorAccessor( + $projector, + (new AttributeProjectorMetadataFactory())->metadata($projector::class), + ); + + $result = $accessor->teardownMethod(); + + self::assertNull($result); + } +} diff --git a/tests/Unit/Repository/MessageDecorator/TraceDecoratorTest.php b/tests/Unit/Repository/MessageDecorator/TraceDecoratorTest.php new file mode 100644 index 000000000..ce613f454 --- /dev/null +++ b/tests/Unit/Repository/MessageDecorator/TraceDecoratorTest.php @@ -0,0 +1,58 @@ +expectException(HeaderNotFound::class); + + $stack = new TraceStack(); + $decorator = new TraceDecorator($stack); + + $message = new Message(new stdClass()); + + $decoratedMessage = $decorator($message); + + self::assertEquals($message, $decoratedMessage); + + $decoratedMessage->header('trace'); + } + + public function testWithTrace(): void + { + $stack = new TraceStack(); + $stack->add(new Trace('name', 'category')); + + $decorator = new TraceDecorator($stack); + + $message = new Message(new stdClass()); + + $decoratedMessage = $decorator($message); + + self::assertEquals( + [ + [ + 'name' => 'name', + 'category' => 'category', + ], + ], + $decoratedMessage->header('trace'), + ); + } +} diff --git a/tests/Unit/Repository/MessageDecorator/TraceStackTest.php b/tests/Unit/Repository/MessageDecorator/TraceStackTest.php new file mode 100644 index 000000000..f348e3534 --- /dev/null +++ b/tests/Unit/Repository/MessageDecorator/TraceStackTest.php @@ -0,0 +1,33 @@ +get()); + + $trace = new Trace('name', 'category'); + + $stack->add($trace); + + self::assertEquals([$trace], $stack->get()); + + $stack->remove($trace); + + self::assertEquals([], $stack->get()); + } +}