Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 105 additions & 102 deletions composer.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ patchlevel_event_sourcing:
store:
type: 'custom' # default is 'dbal'
service: 'my_subscription_store'
options:
table_name: 'my_subscription_store'
```
:::tip
If you are using the [doctrine-test-bundle](https://github.com/dmaicher/doctrine-test-bundle),
Expand Down Expand Up @@ -672,7 +674,7 @@ For this you need to enable the crypto shredding.
```yaml
patchlevel_event_sourcing:
cryptography:
use_encrypted_field_name: true,
use_encrypted_field_name: true
```
:::tip
You should activate `use_encrypted_field_name` to mark the fields that are encrypted.
Expand Down
16 changes: 16 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ patchlevel_event_sourcing:
connection:
url: '%env(EVENTSTORE_URL)%'
provide_dedicated_connection: true
store:
type: dbal_stream
# if you are using doctrine bundle you should enable this
#merge_orm_schema: true
command_bus:
service: messenger.default_bus
query_bus:
service: messenger.default_bus
subscription:
gap_detection: ~

# enable this if you want to use sensitive data encryption
#cryptography: ~
# use_encrypted_field_name: true

when@dev:
patchlevel_event_sourcing:
Expand All @@ -50,6 +64,8 @@ when@dev:
when@test:
patchlevel_event_sourcing:
subscription:
store:
type: 'static_in_memory'
catch_up: true
throw_on_error: true
run_after_aggregate_save: true
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Usage

Here you will find some examples of how to use the bundle.
But we provide only examples for specific symfo
But we provide only examples for specific symfony features.

:::info
You can find out more about event sourcing in the library
Expand Down
60 changes: 60 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,7 +1,67 @@
parameters:
ignoreErrors:
-
message: '#^Parameter \#2 \$id of static method Patchlevel\\EventSourcingBundle\\DependencyInjection\\ServiceAliasResolver\:\:resolve\(\) expects string, array\|bool\|float\|int\|string\|null given\.$#'
identifier: argument.type
count: 1
path: src/DependencyInjection/CommandHandlerCompilerPass.php

-
message: '#^Parameter \#1 \$classString of static method Patchlevel\\EventSourcing\\QueryBus\\HandlerFinder\:\:findInClass\(\) expects class\-string, string given\.$#'
identifier: argument.type
count: 1
path: src/DependencyInjection/QueryHandlerCompilerPass.php

-
message: '#^Parameter \#2 \$id of static method Patchlevel\\EventSourcingBundle\\DependencyInjection\\ServiceAliasResolver\:\:resolve\(\) expects string, array\|bool\|float\|int\|string\|null given\.$#'
identifier: argument.type
count: 1
path: src/DependencyInjection/QueryHandlerCompilerPass.php

-
message: '#^Property Patchlevel\\EventSourcingBundle\\Tests\\Fixtures\\Profile\:\:\$id is never read, only written\.$#'
identifier: property.onlyWritten
count: 1
path: tests/Fixtures/Profile.php

-
message: '#^Property Patchlevel\\EventSourcingBundle\\Tests\\Fixtures\\SnapshotableProfile\:\:\$id is unused\.$#'
identifier: property.unused
count: 1
path: tests/Fixtures/SnapshotableProfile.php

-
message: '#^Call to an undefined method Symfony\\Component\\DependencyInjection\\ContainerBuilder\:\:getAutoconfiguredAttributes\(\)\.$#'
identifier: method.notFound
count: 1
path: tests/Unit/PatchlevelEventSourcingBundleTest.php

-
message: '#^Cannot access offset ''Patchlevel\\\\EventSourcing\\\\Attribute\\\\Aggregate''\|''Patchlevel\\\\EventSourcing\\\\Attribute\\\\Event'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 1
path: tests/Unit/PatchlevelEventSourcingBundleTest.php

-
message: '#^Cannot access offset ''bus'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 3
path: tests/Unit/PatchlevelEventSourcingBundleTest.php

-
message: '#^Cannot access offset ''handles'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 3
path: tests/Unit/PatchlevelEventSourcingBundleTest.php

-
message: '#^Cannot access offset ''method'' on mixed\.$#'
identifier: offsetAccess.nonOffsetAccessible
count: 2
path: tests/Unit/PatchlevelEventSourcingBundleTest.php

-
message: '#^Trying to invoke mixed but it''s not a callable\.$#'
identifier: callable.nonCallable
count: 1
path: tests/Unit/PatchlevelEventSourcingBundleTest.php
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ parameters:
level: max
paths:
- src
- tests
5 changes: 4 additions & 1 deletion src/DependencyInjection/CommandHandlerCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
return;
}

$bus = $container->getParameter('patchlevel_event_sourcing.aggregate_handlers.bus');
$bus = ServiceAliasResolver::resolve(
$container,
$container->getParameter('patchlevel_event_sourcing.aggregate_handlers.bus'),
);

/** @var AggregateRootRegistry $aggregateRootRegistry */
$aggregateRootRegistry = $container->get(AggregateRootRegistry::class);
Expand All @@ -34,7 +37,7 @@
$parameterResolverId = sprintf('.event_sourcing.handler_parameter_resolver.%s', $aggregateName);

foreach (HandlerFinder::findInClass($aggregateClass) as $aggregateHandler) {
$handlerId = strtolower(sprintf('event_sourcing.handler.%s.%s', $aggregateName, $aggregateHandler->method));

Check warning on line 40 in src/DependencyInjection/CommandHandlerCompilerPass.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "UnwrapStrToLower": @@ @@ foreach ($aggregateRootRegistry->aggregateClasses() as $aggregateName => $aggregateClass) { $parameterResolverId = sprintf('.event_sourcing.handler_parameter_resolver.%s', $aggregateName); foreach (HandlerFinder::findInClass($aggregateClass) as $aggregateHandler) { - $handlerId = strtolower(sprintf('event_sourcing.handler.%s.%s', $aggregateName, $aggregateHandler->method)); + $handlerId = sprintf('event_sourcing.handler.%s.%s', $aggregateName, $aggregateHandler->method); $handlerClass = $aggregateHandler->static ? CreateAggregateHandler::class : UpdateAggregateHandler::class; $container->register($handlerId, $handlerClass)->setArguments([new Reference(RepositoryManager::class), $aggregateClass, $aggregateHandler->method, new Reference($parameterResolverId)])->addTag('messenger.message_handler', ['handles' => $aggregateHandler->commandClass, 'bus' => $bus]); }
$handlerClass = $aggregateHandler->static ? CreateAggregateHandler::class : UpdateAggregateHandler::class;

$container->register($handlerId, $handlerClass)
Expand Down
12 changes: 11 additions & 1 deletion src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
* },
* query_bus: array{enabled: bool, service: string},
* subscription: array{
* store: array{type: string, service: string|null},
* store: array{
* type: string,
* service: string|null,
* options: array{table_name: string}
* },
* retry_strategy?: array{base_delay: int, delay_factor: int, max_attempts: int},
* retry_strategies: array<string, array{type: string, service: string, options: array<string, mixed>}>,
* default_retry_strategy: string,
Expand Down Expand Up @@ -194,10 +198,16 @@
->addDefaultsIfNotSet()
->children()
->enumNode('type')
->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])

Check warning on line 201 in src/DependencyInjection/Configuration.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "ArrayItemRemoval": @@ @@ $rootNode = $treeBuilder->getRootNode(); $rootNode->children()->arrayNode('connection')->children()->scalarNode('service')->defaultNull()->end()->scalarNode('url')->defaultNull()->end()->booleanNode('provide_dedicated_connection')->defaultFalse()->end()->end()->end()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->defaultValue('dbal_stream')->end()->scalarNode('service')->defaultNull()->end()->booleanNode('merge_orm_schema')->defaultFalse()->end()->arrayNode('options')->variablePrototype()->end()->end()->booleanNode('read_only')->defaultFalse()->end()->arrayNode('migrate_to_new_store')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->variablePrototype()->end()->end()->arrayNode('translators')->scalarPrototype()->end()->end()->end()->end()->end()->validate()->ifTrue(function (array $v) { return $v['type'] === 'custom' && empty($v['service']); - })->thenInvalid('The "service" field is required when "type" is set to "custom".')->end()->end()->arrayNode('event_bus')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['default', 'symfony', 'psr14', 'custom'])->defaultValue('default')->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('events')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('aggregates')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('headers')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('clock')->addDefaultsIfNotSet()->children()->scalarNode('freeze')->defaultNull()->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('migration')->addDefaultsIfNotSet()->children()->scalarNode('namespace')->defaultValue('EventSourcingMigrations')->end()->scalarNode('path')->defaultValue('%kernel.project_dir%/migrations')->end()->end()->end()->arrayNode('snapshot_stores')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['psr6', 'psr16', 'custom'])->defaultValue('psr6')->end()->scalarNode('service')->end()->end()->end()->end()->arrayNode('subscription')->addDefaultsIfNotSet()->children()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])->defaultValue('dbal')->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->addDefaultsIfNotSet()->children()->scalarNode('table_name')->defaultValue('subscriptions')->end()->end()->end()->end()->end()->arrayNode('retry_strategy')->setDeprecated('patchlevel/event-sourcing-bundle', '3.10', 'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.')->children()->integerNode('base_delay')->defaultValue(5)->end()->integerNode('delay_factor')->defaultValue(2)->end()->integerNode('max_attempts')->defaultValue(5)->end()->end()->end()->arrayNode('retry_strategies')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['clock_based', 'no_retry', 'custom'])->end()->scalarNode('service')->end()->arrayNode('options')->variablePrototype()->end()->end()->end()->end()->defaultValue(['default' => ['type' => 'clock_based', 'options' => ['base_delay' => 5, 'delay_factor' => 2, 'max_attempts' => 5]], 'no_retry' => ['type' => 'no_retry']])->end()->scalarNode('default_retry_strategy')->defaultValue('default')->end()->arrayNode('catch_up')->canBeEnabled()->addDefaultsIfNotSet()->children()->integerNode('limit')->defaultNull()->end()->end()->end()->arrayNode('throw_on_error')->canBeEnabled()->end()->arrayNode('run_after_aggregate_save')->canBeEnabled()->addDefaultsIfNotSet()->children()
->defaultValue('dbal')
->end()
->scalarNode('service')->defaultNull()->end()
->arrayNode('options')
->addDefaultsIfNotSet()
->children()
->scalarNode('table_name')->defaultValue('subscriptions')->end()
->end()
->end()
->end()
->end()

Expand All @@ -208,8 +218,8 @@
'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.'
)
->children()
->integerNode('base_delay')->defaultValue(5)->end()

Check warning on line 221 in src/DependencyInjection/Configuration.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": @@ @@ $rootNode = $treeBuilder->getRootNode(); $rootNode->children()->arrayNode('connection')->children()->scalarNode('service')->defaultNull()->end()->scalarNode('url')->defaultNull()->end()->booleanNode('provide_dedicated_connection')->defaultFalse()->end()->end()->end()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->defaultValue('dbal_stream')->end()->scalarNode('service')->defaultNull()->end()->booleanNode('merge_orm_schema')->defaultFalse()->end()->arrayNode('options')->variablePrototype()->end()->end()->booleanNode('read_only')->defaultFalse()->end()->arrayNode('migrate_to_new_store')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->variablePrototype()->end()->end()->arrayNode('translators')->scalarPrototype()->end()->end()->end()->end()->end()->validate()->ifTrue(function (array $v) { return $v['type'] === 'custom' && empty($v['service']); - })->thenInvalid('The "service" field is required when "type" is set to "custom".')->end()->end()->arrayNode('event_bus')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['default', 'symfony', 'psr14', 'custom'])->defaultValue('default')->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('events')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('aggregates')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('headers')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('clock')->addDefaultsIfNotSet()->children()->scalarNode('freeze')->defaultNull()->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('migration')->addDefaultsIfNotSet()->children()->scalarNode('namespace')->defaultValue('EventSourcingMigrations')->end()->scalarNode('path')->defaultValue('%kernel.project_dir%/migrations')->end()->end()->end()->arrayNode('snapshot_stores')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['psr6', 'psr16', 'custom'])->defaultValue('psr6')->end()->scalarNode('service')->end()->end()->end()->end()->arrayNode('subscription')->addDefaultsIfNotSet()->children()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])->defaultValue('dbal')->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->addDefaultsIfNotSet()->children()->scalarNode('table_name')->defaultValue('subscriptions')->end()->end()->end()->end()->end()->arrayNode('retry_strategy')->setDeprecated('patchlevel/event-sourcing-bundle', '3.10', 'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.')->children()->integerNode('base_delay')->defaultValue(5)->end()->integerNode('delay_factor')->defaultValue(2)->end()->integerNode('max_attempts')->defaultValue(5)->end()->end()->end()->arrayNode('retry_strategies')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['clock_based', 'no_retry', 'custom'])->end()->scalarNode('service')->end()->arrayNode('options')->variablePrototype()->end()->end()->end()->end()->defaultValue(['default' => ['type' => 'clock_based', 'options' => ['base_delay' => 5, 'delay_factor' => 2, 'max_attempts' => 5]], 'no_retry' => ['type' => 'no_retry']])->end()->scalarNode('default_retry_strategy')->defaultValue('default')->end()->arrayNode('catch_up')->canBeEnabled()->addDefaultsIfNotSet()->children()->integerNode('limit')->defaultNull()->end()->end()->end()->arrayNode('throw_on_error')->canBeEnabled()->end()->arrayNode('run_after_aggregate_save')->canBeEnabled()->addDefaultsIfNotSet()->children()

Check warning on line 221 in src/DependencyInjection/Configuration.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "DecrementInteger": @@ @@ $rootNode = $treeBuilder->getRootNode(); $rootNode->children()->arrayNode('connection')->children()->scalarNode('service')->defaultNull()->end()->scalarNode('url')->defaultNull()->end()->booleanNode('provide_dedicated_connection')->defaultFalse()->end()->end()->end()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->defaultValue('dbal_stream')->end()->scalarNode('service')->defaultNull()->end()->booleanNode('merge_orm_schema')->defaultFalse()->end()->arrayNode('options')->variablePrototype()->end()->end()->booleanNode('read_only')->defaultFalse()->end()->arrayNode('migrate_to_new_store')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->variablePrototype()->end()->end()->arrayNode('translators')->scalarPrototype()->end()->end()->end()->end()->end()->validate()->ifTrue(function (array $v) { return $v['type'] === 'custom' && empty($v['service']); - })->thenInvalid('The "service" field is required when "type" is set to "custom".')->end()->end()->arrayNode('event_bus')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['default', 'symfony', 'psr14', 'custom'])->defaultValue('default')->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('events')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('aggregates')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('headers')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('clock')->addDefaultsIfNotSet()->children()->scalarNode('freeze')->defaultNull()->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('migration')->addDefaultsIfNotSet()->children()->scalarNode('namespace')->defaultValue('EventSourcingMigrations')->end()->scalarNode('path')->defaultValue('%kernel.project_dir%/migrations')->end()->end()->end()->arrayNode('snapshot_stores')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['psr6', 'psr16', 'custom'])->defaultValue('psr6')->end()->scalarNode('service')->end()->end()->end()->end()->arrayNode('subscription')->addDefaultsIfNotSet()->children()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])->defaultValue('dbal')->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->addDefaultsIfNotSet()->children()->scalarNode('table_name')->defaultValue('subscriptions')->end()->end()->end()->end()->end()->arrayNode('retry_strategy')->setDeprecated('patchlevel/event-sourcing-bundle', '3.10', 'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.')->children()->integerNode('base_delay')->defaultValue(5)->end()->integerNode('delay_factor')->defaultValue(2)->end()->integerNode('max_attempts')->defaultValue(5)->end()->end()->end()->arrayNode('retry_strategies')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['clock_based', 'no_retry', 'custom'])->end()->scalarNode('service')->end()->arrayNode('options')->variablePrototype()->end()->end()->end()->end()->defaultValue(['default' => ['type' => 'clock_based', 'options' => ['base_delay' => 5, 'delay_factor' => 2, 'max_attempts' => 5]], 'no_retry' => ['type' => 'no_retry']])->end()->scalarNode('default_retry_strategy')->defaultValue('default')->end()->arrayNode('catch_up')->canBeEnabled()->addDefaultsIfNotSet()->children()->integerNode('limit')->defaultNull()->end()->end()->end()->arrayNode('throw_on_error')->canBeEnabled()->end()->arrayNode('run_after_aggregate_save')->canBeEnabled()->addDefaultsIfNotSet()->children()
->integerNode('delay_factor')->defaultValue(2)->end()

Check warning on line 222 in src/DependencyInjection/Configuration.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": @@ @@ $rootNode = $treeBuilder->getRootNode(); $rootNode->children()->arrayNode('connection')->children()->scalarNode('service')->defaultNull()->end()->scalarNode('url')->defaultNull()->end()->booleanNode('provide_dedicated_connection')->defaultFalse()->end()->end()->end()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->defaultValue('dbal_stream')->end()->scalarNode('service')->defaultNull()->end()->booleanNode('merge_orm_schema')->defaultFalse()->end()->arrayNode('options')->variablePrototype()->end()->end()->booleanNode('read_only')->defaultFalse()->end()->arrayNode('migrate_to_new_store')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->variablePrototype()->end()->end()->arrayNode('translators')->scalarPrototype()->end()->end()->end()->end()->end()->validate()->ifTrue(function (array $v) { return $v['type'] === 'custom' && empty($v['service']); - })->thenInvalid('The "service" field is required when "type" is set to "custom".')->end()->end()->arrayNode('event_bus')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['default', 'symfony', 'psr14', 'custom'])->defaultValue('default')->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('events')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('aggregates')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('headers')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('clock')->addDefaultsIfNotSet()->children()->scalarNode('freeze')->defaultNull()->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('migration')->addDefaultsIfNotSet()->children()->scalarNode('namespace')->defaultValue('EventSourcingMigrations')->end()->scalarNode('path')->defaultValue('%kernel.project_dir%/migrations')->end()->end()->end()->arrayNode('snapshot_stores')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['psr6', 'psr16', 'custom'])->defaultValue('psr6')->end()->scalarNode('service')->end()->end()->end()->end()->arrayNode('subscription')->addDefaultsIfNotSet()->children()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])->defaultValue('dbal')->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->addDefaultsIfNotSet()->children()->scalarNode('table_name')->defaultValue('subscriptions')->end()->end()->end()->end()->end()->arrayNode('retry_strategy')->setDeprecated('patchlevel/event-sourcing-bundle', '3.10', 'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.')->children()->integerNode('base_delay')->defaultValue(5)->end()->integerNode('delay_factor')->defaultValue(2)->end()->integerNode('max_attempts')->defaultValue(5)->end()->end()->end()->arrayNode('retry_strategies')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['clock_based', 'no_retry', 'custom'])->end()->scalarNode('service')->end()->arrayNode('options')->variablePrototype()->end()->end()->end()->end()->defaultValue(['default' => ['type' => 'clock_based', 'options' => ['base_delay' => 5, 'delay_factor' => 2, 'max_attempts' => 5]], 'no_retry' => ['type' => 'no_retry']])->end()->scalarNode('default_retry_strategy')->defaultValue('default')->end()->arrayNode('catch_up')->canBeEnabled()->addDefaultsIfNotSet()->children()->integerNode('limit')->defaultNull()->end()->end()->end()->arrayNode('throw_on_error')->canBeEnabled()->end()->arrayNode('run_after_aggregate_save')->canBeEnabled()->addDefaultsIfNotSet()->children()

Check warning on line 222 in src/DependencyInjection/Configuration.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.5, ubuntu-latest)

Escaped Mutant for Mutator "DecrementInteger": @@ @@ $rootNode = $treeBuilder->getRootNode(); $rootNode->children()->arrayNode('connection')->children()->scalarNode('service')->defaultNull()->end()->scalarNode('url')->defaultNull()->end()->booleanNode('provide_dedicated_connection')->defaultFalse()->end()->end()->end()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->defaultValue('dbal_stream')->end()->scalarNode('service')->defaultNull()->end()->booleanNode('merge_orm_schema')->defaultFalse()->end()->arrayNode('options')->variablePrototype()->end()->end()->booleanNode('read_only')->defaultFalse()->end()->arrayNode('migrate_to_new_store')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal_stream', 'dbal_taggable', 'in_memory', 'custom'])->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->variablePrototype()->end()->end()->arrayNode('translators')->scalarPrototype()->end()->end()->end()->end()->end()->validate()->ifTrue(function (array $v) { return $v['type'] === 'custom' && empty($v['service']); - })->thenInvalid('The "service" field is required when "type" is set to "custom".')->end()->end()->arrayNode('event_bus')->canBeEnabled()->addDefaultsIfNotSet()->children()->enumNode('type')->values(['default', 'symfony', 'psr14', 'custom'])->defaultValue('default')->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('events')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('aggregates')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('headers')->beforeNormalization()->castToArray()->end()->defaultValue([])->scalarPrototype()->end()->end()->arrayNode('clock')->addDefaultsIfNotSet()->children()->scalarNode('freeze')->defaultNull()->end()->scalarNode('service')->defaultNull()->end()->end()->end()->arrayNode('migration')->addDefaultsIfNotSet()->children()->scalarNode('namespace')->defaultValue('EventSourcingMigrations')->end()->scalarNode('path')->defaultValue('%kernel.project_dir%/migrations')->end()->end()->end()->arrayNode('snapshot_stores')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['psr6', 'psr16', 'custom'])->defaultValue('psr6')->end()->scalarNode('service')->end()->end()->end()->end()->arrayNode('subscription')->addDefaultsIfNotSet()->children()->arrayNode('store')->addDefaultsIfNotSet()->children()->enumNode('type')->values(['dbal', 'in_memory', 'static_in_memory', 'custom'])->defaultValue('dbal')->end()->scalarNode('service')->defaultNull()->end()->arrayNode('options')->addDefaultsIfNotSet()->children()->scalarNode('table_name')->defaultValue('subscriptions')->end()->end()->end()->end()->end()->arrayNode('retry_strategy')->setDeprecated('patchlevel/event-sourcing-bundle', '3.10', 'The "%node%" option is deprecated and will be removed in 4.0. Use "patchlevel_event_sourcing.subscription.retry_strategies" instead.')->children()->integerNode('base_delay')->defaultValue(5)->end()->integerNode('delay_factor')->defaultValue(2)->end()->integerNode('max_attempts')->defaultValue(5)->end()->end()->end()->arrayNode('retry_strategies')->useAttributeAsKey('name')->arrayPrototype()->children()->enumNode('type')->values(['clock_based', 'no_retry', 'custom'])->end()->scalarNode('service')->end()->arrayNode('options')->variablePrototype()->end()->end()->end()->end()->defaultValue(['default' => ['type' => 'clock_based', 'options' => ['base_delay' => 5, 'delay_factor' => 2, 'max_attempts' => 5]], 'no_retry' => ['type' => 'no_retry']])->end()->scalarNode('default_retry_strategy')->defaultValue('default')->end()->arrayNode('catch_up')->canBeEnabled()->addDefaultsIfNotSet()->children()->integerNode('limit')->defaultNull()->end()->end()->end()->arrayNode('throw_on_error')->canBeEnabled()->end()->arrayNode('run_after_aggregate_save')->canBeEnabled()->addDefaultsIfNotSet()->children()
->integerNode('max_attempts')->defaultValue(5)->end()
->end()
->end()
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/PatchlevelEventSourcingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ static function (ChildDefinition $definition): void {
$container->register(DoctrineSubscriptionStore::class)
->setArguments([
new Reference('event_sourcing.dbal_connection'),
new Reference('event_sourcing.clock'),
$config['subscription']['store']['options']['table_name'],
])
->addTag('event_sourcing.doctrine_schema_configurator');

Expand Down
5 changes: 4 additions & 1 deletion src/DependencyInjection/QueryHandlerCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public function process(ContainerBuilder $container): void
return;
}

$bus = $container->getParameter('patchlevel_event_sourcing.query_handlers.bus');
$bus = ServiceAliasResolver::resolve(
$container,
$container->getParameter('patchlevel_event_sourcing.query_handlers.bus'),
);
$subscribers = $container->findTaggedServiceIds('event_sourcing.subscriber');

foreach (array_keys($subscribers) as $subscriberServiceName) {
Expand Down
23 changes: 23 additions & 0 deletions src/DependencyInjection/ServiceAliasResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcingBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;

/** @interal */
final readonly class ServiceAliasResolver
{
public static function resolve(ContainerBuilder $container, string $id): string
{
if ($container->hasAlias($id)) {
return self::resolve(
$container,
(string)$container->getAlias($id),
);
}

return $id;
}
}
16 changes: 2 additions & 14 deletions src/DependencyInjection/SubscriberGuardCompilePass.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function process(ContainerBuilder $container): void
);
}

$subscriptionConnection = $this->resolveService($container, (string)$argument);
$subscriptionConnection = ServiceAliasResolver::resolve($container, (string)$argument);

$subscribers = $container->findTaggedServiceIds('event_sourcing.subscriber');

Expand All @@ -48,7 +48,7 @@ public function process(ContainerBuilder $container): void
continue;
}

if ($subscriptionConnection !== $this->resolveService($container, (string)$argument)) {
if ($subscriptionConnection !== ServiceAliasResolver::resolve($container, (string)$argument)) {
continue;
}

Expand All @@ -68,16 +68,4 @@ public function process(ContainerBuilder $container): void
}
}
}

private function resolveService(ContainerBuilder $container, string $id): string
{
if ($container->hasAlias($id)) {
return $this->resolveService(
$container,
(string)$container->getAlias($id),
);
}

return $id;
}
}
1 change: 1 addition & 0 deletions tests/Fixtures/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Profile extends BasicAggregateRoot
#[Id]
private CustomId $id;

/** @param Repository<self> $profileRepository */
#[Handle]
public static function create(
CreateProfile $command,
Expand Down
1 change: 0 additions & 1 deletion tests/Unit/DataCollector/EventSourcingCollectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public function testCollectData(): void
self::assertEquals(ProfileCreated::class, $message['event_class']);
self::assertEquals('profile.created', $message['event_name']);
self::assertInstanceOf(Data::class, $message['event']);
self::assertIsArray($message['headers']);
self::assertCount(3, $message['headers']);
self::assertInstanceOf(Data::class, $message['headers'][0]);
}
Expand Down
4 changes: 4 additions & 0 deletions tests/Unit/PatchlevelEventSourcingBundleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
use Patchlevel\EventSourcing\Subscription\Store\SubscriptionStore;
use Patchlevel\EventSourcing\Subscription\Subscriber\MetadataSubscriberAccessorRepository;
use Patchlevel\EventSourcingBundle\Command\StoreMigrateCommand;
use Patchlevel\EventSourcingBundle\DependencyInjection\Configuration;
use Patchlevel\EventSourcingBundle\DependencyInjection\PatchlevelEventSourcingExtension;
use Patchlevel\EventSourcingBundle\EventBus\SymfonyEventBus;
use Patchlevel\EventSourcingBundle\PatchlevelEventSourcingBundle;
Expand Down Expand Up @@ -1553,6 +1554,9 @@ public function testNamedRepository(): void
self::assertSame($profileRepository, $namedArgumentProfileRepository);
}

/**
* @param array{patchlevel_event_sourcing: array<string, mixed>} $config
*/
private function compileContainer(ContainerBuilder $container, array $config): void
{
$bundle = new PatchlevelEventSourcingBundle();
Expand Down
Loading