Skip to content

Commit ff0ca76

Browse files
authored
Merge branch 'main' into projecting/multi-streams
2 parents 6ad3c79 + 71b1162 commit ff0ca76

File tree

16 files changed

+101
-50
lines changed

16 files changed

+101
-50
lines changed

packages/Amqp/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
},
4343
"require": {
4444
"ext-amqp": "*",
45-
"ecotone/enqueue": "~1.286.0",
45+
"ecotone/enqueue": "~1.286.1",
4646
"enqueue/amqp-ext": "^0.10.18",
4747
"enqueue/dsn": "^0.10.4",
4848
"enqueue/enqueue": "^0.10.0"
@@ -84,7 +84,7 @@
8484
},
8585
"extra": {
8686
"branch-alias": {
87-
"dev-main": "1.286.0-dev"
87+
"dev-main": "1.286.1-dev"
8888
},
8989
"ecotone": {
9090
"repository": "amqp"
@@ -105,7 +105,7 @@
105105
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
106106
}
107107
},
108-
"release-time": "2025-12-22 18:48:46"
108+
"release-time": "2025-12-23 19:28:33"
109109
},
110110
"config": {
111111
"allow-plugins": {

packages/Dbal/composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
}
3535
},
3636
"require": {
37-
"ecotone/enqueue": "~1.286.0",
37+
"ecotone/enqueue": "~1.286.1",
3838
"doctrine/dbal": "^3.9|^4.0",
3939
"enqueue/dsn": "^0.10.4"
4040
},
@@ -46,7 +46,7 @@
4646
"doctrine/cache": "^1.0.0|^2.0",
4747
"doctrine/annotations": "^1.13|^2.0",
4848
"wikimedia/composer-merge-plugin": "^2.1",
49-
"ecotone/jms-converter": "~1.286.0",
49+
"ecotone/jms-converter": "~1.286.1",
5050
"symfony/expression-language": "^6.4|^7.0|^8.0"
5151
},
5252
"scripts": {
@@ -59,7 +59,7 @@
5959
},
6060
"extra": {
6161
"branch-alias": {
62-
"dev-main": "1.286.0-dev"
62+
"dev-main": "1.286.1-dev"
6363
},
6464
"ecotone": {
6565
"repository": "dbal"
@@ -80,7 +80,7 @@
8080
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
8181
}
8282
},
83-
"release-time": "2025-12-22 18:48:46"
83+
"release-time": "2025-12-23 19:28:33"
8484
},
8585
"config": {
8686
"allow-plugins": {

packages/Ecotone/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
},
6868
"extra": {
6969
"branch-alias": {
70-
"dev-main": "1.286.0-dev"
70+
"dev-main": "1.286.1-dev"
7171
},
7272
"ecotone": {
7373
"repository": "ecotone"
@@ -88,7 +88,7 @@
8888
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
8989
}
9090
},
91-
"release-time": "2025-12-22 18:48:46"
91+
"release-time": "2025-12-23 19:28:33"
9292
},
9393
"config": {
9494
"sort-packages": true,

packages/Ecotone/src/Modelling/Api/Distribution/DistributedServiceMap.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class DistributedServiceMap implements DefinedObject
2525
{
2626
/**
2727
* @param array<string, string> $commandMapping - service name -> channel name (for command routing)
28-
* @param array<string, array{keys: ?array<string>, exclude: array<string>}> $eventSubscriptions - channel name -> ['keys' => [...] or null, 'exclude' => [...]]
28+
* @param array<string, array{keys: ?array<string>, exclude: array<string>, include: array<string>}> $eventSubscriptions - channel name -> ['keys' => [...] or null, 'exclude' => [...], 'include' => [...]]
2929
* @param array<object> $distributedBusAnnotations
3030
* @param bool|null $legacyMode - null = not set, true = legacy (withServiceMapping), false = new API (withCommandMapping/withEventMapping)
3131
*/
@@ -58,6 +58,7 @@ public function withServiceMapping(string $serviceName, string $channelName, ?ar
5858
$self->eventSubscriptions[$channelName] = [
5959
'keys' => $subscriptionRoutingKeys,
6060
'exclude' => [],
61+
'include' => [],
6162
];
6263

6364
return $self;
@@ -83,17 +84,26 @@ public function withCommandMapping(string $targetServiceName, string $channelNam
8384
*
8485
* @param string $channelName Target channel to send events to
8586
* @param array<string> $subscriptionKeys Routing key patterns to match
86-
* @param array<string> $excludeEventsFromServices Service names whose events should NOT be sent to this channel
87+
* @param array<string> $excludePublishingServices Service names whose events should NOT be sent to this channel
88+
* @param array<string> $includePublishingServices Service names whose events should ONLY be sent to this channel (whitelist)
8789
*/
88-
public function withEventMapping(string $channelName, array $subscriptionKeys, array $excludeEventsFromServices = []): self
90+
public function withEventMapping(string $channelName, array $subscriptionKeys, array $excludePublishingServices = [], array $includePublishingServices = []): self
8991
{
92+
if ($excludePublishingServices !== [] && $includePublishingServices !== []) {
93+
throw ConfigurationException::create(
94+
"Cannot use both 'excludePublishingServices' and 'includePublishingServices' in the same event mapping for channel '{$channelName}'. " .
95+
'These parameters are mutually exclusive - use either exclude (blacklist) or include (whitelist), not both.'
96+
);
97+
}
98+
9099
$self = clone $this;
91100
$self->assertNotInLegacyMode('withEventMapping');
92101
$self->legacyMode = false;
93102

94103
$self->eventSubscriptions[$channelName] = [
95104
'keys' => $subscriptionKeys,
96-
'exclude' => $excludeEventsFromServices,
105+
'exclude' => $excludePublishingServices,
106+
'include' => $includePublishingServices,
97107
];
98108

99109
return $self;
@@ -153,7 +163,7 @@ public function getAllChannelNamesBesides(string $serviceName, string $routingKe
153163

154164
/**
155165
* NEW MODE ONLY - Get all subscription channels for an event.
156-
* Uses explicit exclude list from eventSubscriptions config.
166+
* Uses explicit exclude/include list from eventSubscriptions config.
157167
*
158168
* @param string $sourceServiceName The service publishing the event
159169
* @param string $routingKey The event routing key
@@ -166,11 +176,16 @@ public function getAllSubscriptionChannels(string $sourceServiceName, string $ro
166176
foreach ($this->eventSubscriptions as $channel => $config) {
167177
$keys = $config['keys'];
168178
$exclude = $config['exclude'];
179+
$include = $config['include'];
169180

170181
if (in_array($sourceServiceName, $exclude, true)) {
171182
continue;
172183
}
173184

185+
if ($include !== [] && ! in_array($sourceServiceName, $include, true)) {
186+
continue;
187+
}
188+
174189
foreach ($keys as $subscriptionEventFilter) {
175190
if (BusRoutingMap::globMatch($subscriptionEventFilter, $routingKey)) {
176191
$filteredChannels[] = $channel;

packages/Ecotone/tests/Messaging/Unit/Distributed/DistributedBusWithExplicitServiceMapTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public function test_it_does_not_publish_event_to_publishing_service_when_exclud
290290
->withCommandMapping(targetServiceName: TestServiceName::TICKET_SERVICE, channelName: $ticketChannelName)
291291
->withCommandMapping(targetServiceName: TestServiceName::USER_SERVICE, channelName: $userChannelName)
292292
->withEventMapping(channelName: $ticketChannelName, subscriptionKeys: ['*'])
293-
->withEventMapping(channelName: $userChannelName, subscriptionKeys: ['*'], excludeEventsFromServices: [TestServiceName::USER_SERVICE])
293+
->withEventMapping(channelName: $userChannelName, subscriptionKeys: ['*'], excludePublishingServices: [TestServiceName::USER_SERVICE])
294294
);
295295
$ticketService = $this->bootstrapEcotone(TestServiceName::TICKET_SERVICE, ['Test\Ecotone\Messaging\Fixture\Distributed\DistributedEventBus\ReceiverTicket'], [new \Test\Ecotone\Messaging\Fixture\Distributed\DistributedEventBus\ReceiverTicket\TicketServiceReceiver()], $distributedTicketQueue);
296296

@@ -851,4 +851,40 @@ public function test_cannot_use_with_event_mapping_after_legacy_with_service_map
851851
->withServiceMapping('service1', 'channel1')
852852
->withEventMapping('channel2', ['*']);
853853
}
854+
855+
public function test_it_publishes_event_only_to_channel_when_source_service_is_in_include_list(): void
856+
{
857+
$distributedTicketQueue = SimpleMessageChannelBuilder::createQueueChannel($ticketChannelName = 'distributed_ticket_channel');
858+
$distributedUserQueue = SimpleMessageChannelBuilder::createQueueChannel($userChannelName = 'distributed_user_channel');
859+
$userService = $this->bootstrapEcotone(
860+
TestServiceName::USER_SERVICE,
861+
[],
862+
[],
863+
[$distributedTicketQueue, $distributedUserQueue],
864+
DistributedServiceMap::initialize()
865+
->withCommandMapping(targetServiceName: TestServiceName::TICKET_SERVICE, channelName: $ticketChannelName)
866+
->withCommandMapping(targetServiceName: TestServiceName::USER_SERVICE, channelName: $userChannelName)
867+
->withEventMapping(channelName: $ticketChannelName, subscriptionKeys: ['*'], includePublishingServices: [TestServiceName::TICKET_SERVICE])
868+
->withEventMapping(channelName: $userChannelName, subscriptionKeys: ['*'])
869+
);
870+
$ticketService = $this->bootstrapEcotone(TestServiceName::TICKET_SERVICE, ['Test\Ecotone\Messaging\Fixture\Distributed\DistributedEventBus\ReceiverTicket'], [new \Test\Ecotone\Messaging\Fixture\Distributed\DistributedEventBus\ReceiverTicket\TicketServiceReceiver()], $distributedTicketQueue);
871+
872+
$userService->getDistributedBus()->publishEvent(
873+
'userService.billing.DetailsWereChanged',
874+
'User changed billing address',
875+
metadata: ['token' => '123']
876+
);
877+
878+
self::assertNull($ticketService->getMessageChannel($ticketChannelName)->receive());
879+
self::assertNotNull($userService->getMessageChannel($userChannelName)->receive());
880+
}
881+
882+
public function test_cannot_use_both_exclude_and_include_publishing_services(): void
883+
{
884+
$this->expectException(ConfigurationException::class);
885+
$this->expectExceptionMessage("Cannot use both 'excludePublishingServices' and 'includePublishingServices' in the same event mapping for channel 'channel1'. These parameters are mutually exclusive - use either exclude (blacklist) or include (whitelist), not both.");
886+
887+
DistributedServiceMap::initialize()
888+
->withEventMapping('channel1', ['*'], excludePublishingServices: ['service1'], includePublishingServices: ['service2']);
889+
}
854890
}

packages/Enqueue/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
}
3434
},
3535
"require": {
36-
"ecotone/ecotone": "~1.286.0",
36+
"ecotone/ecotone": "~1.286.1",
3737
"queue-interop/queue-interop": "^0.8",
3838
"enqueue/dsn": "^0.10.4"
3939
},
@@ -56,7 +56,7 @@
5656
},
5757
"extra": {
5858
"branch-alias": {
59-
"dev-main": "1.286.0-dev"
59+
"dev-main": "1.286.1-dev"
6060
},
6161
"ecotone": {
6262
"repository": "enqueue"
@@ -77,7 +77,7 @@
7777
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
7878
}
7979
},
80-
"release-time": "2025-12-22 18:48:46"
80+
"release-time": "2025-12-23 19:28:33"
8181
},
8282
"config": {
8383
"allow-plugins": {

packages/JmsConverter/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
}
3939
},
4040
"require": {
41-
"ecotone/ecotone": "~1.286.0",
41+
"ecotone/ecotone": "~1.286.1",
4242
"jms/serializer": "^3.32",
4343
"symfony/cache": "^6.4|^7.0|^8.0"
4444
},
@@ -60,7 +60,7 @@
6060
},
6161
"extra": {
6262
"branch-alias": {
63-
"dev-main": "1.286.0-dev"
63+
"dev-main": "1.286.1-dev"
6464
},
6565
"ecotone": {
6666
"repository": "jms-converter"
@@ -81,7 +81,7 @@
8181
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
8282
}
8383
},
84-
"release-time": "2025-12-22 18:48:46"
84+
"release-time": "2025-12-23 19:28:33"
8585
},
8686
"config": {
8787
"sort-packages": true,

packages/Kafka/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"require": {
3535
"ext-rdkafka": "*",
36-
"ecotone/ecotone": "~1.286.0"
36+
"ecotone/ecotone": "~1.286.1"
3737
},
3838
"require-dev": {
3939
"phpunit/phpunit": "^10.5|^11.0",
@@ -52,7 +52,7 @@
5252
},
5353
"extra": {
5454
"branch-alias": {
55-
"dev-main": "1.286.0-dev"
55+
"dev-main": "1.286.1-dev"
5656
},
5757
"ecotone": {
5858
"repository": "kafka"
@@ -73,7 +73,7 @@
7373
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
7474
}
7575
},
76-
"release-time": "2025-12-22 18:48:46"
76+
"release-time": "2025-12-23 19:28:33"
7777
},
7878
"config": {
7979
"allow-plugins": {

packages/Laravel/composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
}
3939
},
4040
"require": {
41-
"ecotone/ecotone": "~1.286.0",
41+
"ecotone/ecotone": "~1.286.1",
4242
"laravel/framework": "^9.5.2|^10.0|^11.0|^12.0|^13.0"
4343
},
4444
"require-dev": {
@@ -51,7 +51,7 @@
5151
"symfony/expression-language": "^6.4|^7.0|^8.0",
5252
"nesbot/carbon": "^2.71|^3.0",
5353
"moneyphp/money": "^4.1.0",
54-
"ecotone/dbal": "~1.286.0",
54+
"ecotone/dbal": "~1.286.1",
5555
"timacdonald/log-fake": "^2.0"
5656
},
5757
"extra": {
@@ -61,7 +61,7 @@
6161
]
6262
},
6363
"branch-alias": {
64-
"dev-main": "1.286.0-dev"
64+
"dev-main": "1.286.1-dev"
6565
},
6666
"ecotone": {
6767
"repository": "laravel"
@@ -82,7 +82,7 @@
8282
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
8383
}
8484
},
85-
"release-time": "2025-12-22 18:48:46"
85+
"release-time": "2025-12-23 19:28:33"
8686
},
8787
"scripts": {
8888
"tests:phpstan": "vendor/bin/phpstan",

packages/LiteApplication/composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
}
3737
},
3838
"require": {
39-
"ecotone/ecotone": "~1.286.0",
40-
"ecotone/jms-converter": "~1.286.0",
39+
"ecotone/ecotone": "~1.286.1",
40+
"ecotone/jms-converter": "~1.286.1",
4141
"php-di/php-di": "^7.0.5"
4242
},
4343
"require-dev": {
@@ -50,7 +50,7 @@
5050
},
5151
"extra": {
5252
"branch-alias": {
53-
"dev-main": "1.286.0-dev"
53+
"dev-main": "1.286.1-dev"
5454
},
5555
"ecotone": {
5656
"repository": "lite-application"
@@ -71,7 +71,7 @@
7171
"description": "Allows to use Enterprise features of Ecotone. For more information please write to [email protected]"
7272
}
7373
},
74-
"release-time": "2025-12-22 18:48:46"
74+
"release-time": "2025-12-23 19:28:33"
7575
},
7676
"scripts": {
7777
"tests:phpstan": "vendor/bin/phpstan",

0 commit comments

Comments
 (0)