Skip to content

Commit 5c5b7e0

Browse files
committed
allow passing metadata matcher with projection running configuration
1 parent 9f021c8 commit 5c5b7e0

File tree

9 files changed

+153
-8
lines changed

9 files changed

+153
-8
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@
124124
"laminas/laminas-code": "^4",
125125
"jms/serializer": "^3.32",
126126
"laravel/framework": "^9.5.2|^10.0|^11.0|^12.0|^13.0",
127-
"prooph/event-store": "<=7.11.1",
128-
"prooph/pdo-event-store": "^1.15.1 <1.16.0",
127+
"prooph/pdo-event-store": "^1.16.2",
129128
"psr/log": "^2.0|^3.0",
130129
"queue-interop/queue-interop": "^0.8",
131130
"ramsey/uuid": "^4.0",

packages/PdoEventSourcing/composer.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@
3434
},
3535
"require": {
3636
"ecotone/dbal": "~1.252.0",
37-
"prooph/event-store": "<=7.11.1",
38-
"prooph/pdo-event-store": "^1.15.1 <1.16.0"
37+
"prooph/pdo-event-store": "^1.16.2"
3938
},
4039
"require-dev": {
4140
"phpunit/phpunit": "^9.6|^10.5|^11.0",
@@ -80,4 +79,4 @@
8079
"wikimedia/composer-merge-plugin": true
8180
}
8281
}
83-
}
82+
}

packages/PdoEventSourcing/src/CachingInMemoryReadModelProjector.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Ecotone\EventSourcing;
66

77
use Closure;
8+
use Prooph\EventStore\Metadata\MetadataMatcher;
89
use Prooph\EventStore\Projection\ReadModel;
910
use Prooph\EventStore\Projection\ReadModelProjector;
1011

@@ -27,6 +28,13 @@ public function init(Closure $callback): ReadModelProjector
2728
return $this;
2829
}
2930

31+
public function withMetadataMatcher(?MetadataMatcher $metadataMatcher = null): ReadModelProjector
32+
{
33+
$this->inMemoryEventStoreReadModelProjector->withMetadataMatcher($metadataMatcher);
34+
35+
return $this;
36+
}
37+
3038
public function fromStream(string $streamName): ReadModelProjector
3139
{
3240
if ($this->isFromStreamSetup) {

packages/PdoEventSourcing/src/InMemory/CachingInMemoryProjectionManager.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Ecotone\EventSourcing\InMemory;
66

77
use Ecotone\EventSourcing\CachingInMemoryReadModelProjector;
8+
use Prooph\EventStore\Projection\ProjectionManager;
89
use Prooph\EventStore\Projection\ProjectionStatus;
910
use Prooph\EventStore\Projection\Projector;
1011
use Prooph\EventStore\Projection\Query;
@@ -14,7 +15,7 @@
1415
/**
1516
* licence Apache-2.0
1617
*/
17-
class CachingInMemoryProjectionManager implements \Prooph\EventStore\Projection\ProjectionManager
18+
class CachingInMemoryProjectionManager implements ProjectionManager
1819
{
1920
/**
2021
* @var ReadModelProjector[]

packages/PdoEventSourcing/src/ProjectionRunningConfiguration.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace Ecotone\EventSourcing;
44

5+
use Assert\Assertion;
56
use Ecotone\Messaging\Config\Container\DefinedObject;
67
use Ecotone\Messaging\Config\Container\Definition;
78
use InvalidArgumentException;
9+
use Prooph\EventStore\Metadata\MetadataMatcher;
810

911
/**
1012
* licence Apache-2.0
@@ -35,6 +37,9 @@ class ProjectionRunningConfiguration implements DefinedObject
3537
public const OPTION_LOAD_COUNT = 'load_count';
3638
public const DEFAULT_LOAD_COUNT = null;
3739

40+
public const OPTION_METADATA_MATCHER = 'metadata_matcher';
41+
public const DEFAULT_METADATA_MATCHER = null;
42+
3843
public const OPTION_IS_TESTING_SETUP = 'isTestingSetup';
3944
public const DEFAULT_IS_TESTING_SETUP = false;
4045

@@ -54,6 +59,7 @@ public function __construct(
5459
self::OPTION_UPDATE_LOCK_TIMEOUT_AFTER => self::DEFAULT_UPDATE_LOCK_TIMEOUT_AFTER,
5560
self::OPTION_IS_TESTING_SETUP => self::DEFAULT_IS_TESTING_SETUP,
5661
self::OPTION_LOAD_COUNT => self::DEFAULT_LOAD_COUNT,
62+
self::OPTION_METADATA_MATCHER => self::DEFAULT_METADATA_MATCHER,
5763
];
5864
}
5965

@@ -100,6 +106,10 @@ public function isTestingSetup(): bool
100106

101107
public function withOption(string $key, mixed $value): static
102108
{
109+
if ($key === self::OPTION_METADATA_MATCHER && $value !== null) {
110+
Assertion::isInstanceOf($value, MetadataMatcher::class);
111+
}
112+
103113
$self = clone $this;
104114
$self->options[$key] = $value;
105115

packages/PdoEventSourcing/src/ProjectionSetupConfiguration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Ecotone\EventSourcing;
44

5+
use Assert\Assertion;
56
use Ecotone\Messaging\Config\Container\DefinedObject;
67
use Ecotone\Messaging\Config\Container\Definition;
78
use Ecotone\Messaging\NullableMessageChannel;

packages/PdoEventSourcing/src/Prooph/LazyProophProjectionManager.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Ecotone\EventSourcing\EventSourcingConfiguration;
66
use Ecotone\EventSourcing\ProjectionExecutor;
7+
use Ecotone\EventSourcing\ProjectionRunningConfiguration;
78
use Ecotone\EventSourcing\ProjectionSetupConfiguration;
89
use Ecotone\EventSourcing\ProjectionStreamSource;
910
use Ecotone\Messaging\Gateway\MessagingEntrypoint;
@@ -12,9 +13,12 @@
1213
use Prooph\Common\Messaging\Message;
1314
use Prooph\EventStore\Exception\ProjectionNotFound;
1415
use Prooph\EventStore\Exception\RuntimeException;
16+
use Prooph\EventStore\Metadata\MetadataMatcher;
1517
use Prooph\EventStore\Pdo\Projection\MariaDbProjectionManager;
1618
use Prooph\EventStore\Pdo\Projection\MySqlProjectionManager;
1719
use Prooph\EventStore\Pdo\Projection\PostgresProjectionManager;
20+
use Prooph\EventStore\Projection\MetadataAwareProjector;
21+
use Prooph\EventStore\Projection\MetadataAwareReadModelProjector;
1822
use Prooph\EventStore\Projection\ProjectionManager;
1923
use Prooph\EventStore\Projection\ProjectionStatus;
2024
use Prooph\EventStore\Projection\Projector;
@@ -74,12 +78,28 @@ public function createQuery(): Query
7478

7579
public function createProjection(string $name, array $options = []): Projector
7680
{
77-
return $this->getProjectionManager()->createProjection($name, $options);
81+
$projection = $this->getProjectionManager()->createProjection($name, $options);
82+
83+
$metadataMatcher = $options[ProjectionRunningConfiguration::OPTION_METADATA_MATCHER] ?? null;
84+
85+
if ($metadataMatcher instanceof MetadataMatcher && $projection instanceof MetadataAwareProjector) {
86+
$projection = $projection->withMetadataMatcher($metadataMatcher);
87+
}
88+
89+
return $projection;
7890
}
7991

8092
public function createReadModelProjection(string $name, ReadModel $readModel, array $options = []): ReadModelProjector
8193
{
82-
return $this->getProjectionManager()->createReadModelProjection($name, $readModel, $options);
94+
$projection = $this->getProjectionManager()->createReadModelProjection($name, $readModel, $options);
95+
96+
$metadataMatcher = $options[ProjectionRunningConfiguration::OPTION_METADATA_MATCHER] ?? null;
97+
98+
if ($metadataMatcher instanceof MetadataMatcher && $projection instanceof MetadataAwareReadModelProjector) {
99+
$projection = $projection->withMetadataMatcher($metadataMatcher);
100+
}
101+
102+
return $projection;
83103
}
84104

85105
public function deleteProjection(string $name, bool $deleteEmittedEvents): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Test\Ecotone\EventSourcing\Fixture\ProjectionWithMetadataMatcher;
6+
7+
use Ecotone\EventSourcing\ProjectionRunningConfiguration;
8+
use Ecotone\Messaging\Attribute\ServiceContext;
9+
use Ecotone\Messaging\Endpoint\PollingMetadata;
10+
use Prooph\EventStore\Metadata\FieldType;
11+
use Prooph\EventStore\Metadata\MetadataMatcher;
12+
use Prooph\EventStore\Metadata\Operator;
13+
use Prooph\EventStore\Pdo\Projection\GapDetection;
14+
use Prooph\EventStore\Pdo\Projection\PdoEventStoreReadModelProjector;
15+
use Test\Ecotone\EventSourcing\Fixture\TicketWithPollingProjection\InProgressTicketList;
16+
17+
final class ProjectionWithMetadataMatcherConfig
18+
{
19+
#[ServiceContext]
20+
public function enablePollingProjection(): ProjectionRunningConfiguration
21+
{
22+
$metadataMatcher = (new MetadataMatcher())
23+
->withMetadataMatch('test', Operator::EQUALS(), 'false', FieldType::METADATA())
24+
;
25+
26+
return ProjectionRunningConfiguration::createPolling(InProgressTicketList::IN_PROGRESS_TICKET_PROJECTION)
27+
->withTestingSetup()
28+
->withOption(PdoEventStoreReadModelProjector::OPTION_GAP_DETECTION, null)
29+
->withOption(ProjectionRunningConfiguration::OPTION_METADATA_MATCHER, $metadataMatcher);
30+
}
31+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Test\Ecotone\EventSourcing\Integration;
6+
7+
use Ecotone\EventSourcing\EventSourcingConfiguration;
8+
use Ecotone\Lite\EcotoneLite;
9+
use Ecotone\Messaging\Config\ServiceConfiguration;
10+
use Enqueue\Dbal\DbalConnectionFactory;
11+
use Test\Ecotone\EventSourcing\EventSourcingMessagingTestCase;
12+
use Test\Ecotone\EventSourcing\Fixture\Ticket\Command\CloseTicket;
13+
use Test\Ecotone\EventSourcing\Fixture\Ticket\Command\RegisterTicket;
14+
use Test\Ecotone\EventSourcing\Fixture\Ticket\TicketEventConverter;
15+
use Test\Ecotone\EventSourcing\Fixture\TicketWithPollingProjection\InProgressTicketList;
16+
use Test\Ecotone\EventSourcing\Fixture\TicketWithPollingProjection\ProjectionConfiguration;
17+
18+
final class ProjectionWithMetadataMatcherTest extends EventSourcingMessagingTestCase
19+
{
20+
public function test_configured_metadata_matcher_is_used(): void
21+
{
22+
$connectionFactory = self::getConnectionFactory();
23+
$connection = $connectionFactory->createContext()
24+
->getDbalConnection()
25+
;
26+
27+
$ecotoneLite = EcotoneLite::bootstrapFlowTestingWithEventStore(
28+
classesToResolve: [ProjectionConfiguration::class, InProgressTicketList::class],
29+
containerOrAvailableServices: [new InProgressTicketList($connection), new TicketEventConverter(), DbalConnectionFactory::class => $connectionFactory],
30+
configuration: ServiceConfiguration::createWithDefaults()
31+
->withEnvironment('prod')
32+
->withNamespaces([
33+
'Test\Ecotone\EventSourcing\Fixture\Ticket',
34+
'Test\Ecotone\EventSourcing\Fixture\ProjectionWithMetadataMatcher',
35+
])
36+
->withExtensionObjects([
37+
EventSourcingConfiguration::createWithDefaults(),
38+
]),
39+
pathToRootCatalog: __DIR__ . '/../../',
40+
runForProductionEventStore: true
41+
);
42+
43+
$ecotoneLite->initializeProjection(InProgressTicketList::IN_PROGRESS_TICKET_PROJECTION);
44+
45+
$ecotoneLite->sendCommand(new RegisterTicket('123', 'Johnny', 'alert'), metadata: ['test' => 'false']);
46+
$ecotoneLite->sendCommand(new RegisterTicket('124', 'Johnny', 'alert'), metadata: ['test' => 'false']);
47+
$ecotoneLite->sendCommand(new RegisterTicket('125', 'Johnny', 'alert'), metadata: ['test' => 'true']);
48+
$ecotoneLite->sendCommand(new RegisterTicket('126', 'Johnny', 'alert'), metadata: ['test' => 'false']);
49+
$ecotoneLite->sendCommand(new RegisterTicket('127', 'Johnny', 'alert'), metadata: ['test' => 'true']);
50+
51+
self::assertEquals([], $ecotoneLite->sendQueryWithRouting('getInProgressTickets'));
52+
53+
$ecotoneLite->run(InProgressTicketList::IN_PROGRESS_TICKET_PROJECTION);
54+
55+
self::assertEquals([
56+
['ticket_id' => '123', 'ticket_type' => 'alert'],
57+
['ticket_id' => '124', 'ticket_type' => 'alert'],
58+
['ticket_id' => '126', 'ticket_type' => 'alert'],
59+
], $ecotoneLite->sendQueryWithRouting('getInProgressTickets'));
60+
61+
$ecotoneLite->sendCommand(new CloseTicket('123'), metadata: ['test' => 'false']);
62+
63+
self::assertEquals([
64+
['ticket_id' => '123', 'ticket_type' => 'alert'],
65+
['ticket_id' => '124', 'ticket_type' => 'alert'],
66+
['ticket_id' => '126', 'ticket_type' => 'alert'],
67+
], $ecotoneLite->sendQueryWithRouting('getInProgressTickets'));
68+
69+
$ecotoneLite->run(InProgressTicketList::IN_PROGRESS_TICKET_PROJECTION);
70+
71+
self::assertEquals([
72+
['ticket_id' => '124', 'ticket_type' => 'alert'],
73+
['ticket_id' => '126', 'ticket_type' => 'alert'],
74+
], $ecotoneLite->sendQueryWithRouting('getInProgressTickets'));
75+
}
76+
}

0 commit comments

Comments
 (0)