Skip to content

Commit

Permalink
throw multiple handlers error when not allowed on compiler time
Browse files Browse the repository at this point in the history
  • Loading branch information
yceruto committed Aug 4, 2024
1 parent 2fb97e9 commit bb08c97
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,42 @@
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;

final readonly class MessageHandlersLocatorPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;

public function __construct(
private string $messageHandlerTagName,
private string $messageHandlerMiddlewareId,
private string $handlingMiddlewareId,
private array $exclude = [],
private bool $allowMultiple = false,
private string $topic = 'message',
) {
}

public function process(ContainerBuilder $container): void
{
if (!$container->has($this->messageHandlerMiddlewareId)) {
if (!$container->has($this->handlingMiddlewareId)) {
return;
}

$handlers = $this->findAndSortTaggedServices(
new TaggedIteratorArgument($this->messageHandlerTagName, 'class'),
$container,
[],
$this->allowMultiple,
tagName: new TaggedIteratorArgument($this->messageHandlerTagName, 'class'),
container: $container,
exclude: $this->exclude,
);

if ($this->allowMultiple) {
$refs = $handlers;
} else {
$refs = [];
foreach ($handlers as $class => $reference) {
$refs[$class][] = $reference;
if (!$this->allowMultiple) {
foreach ($handlers as $class => $refs) {
if (count($refs) > 1) {
throw new LogicException(sprintf('Only one handler is allowed for %s of type "%s".', $this->topic, $class));
}
}
}

$middleware = $container->findDefinition($this->messageHandlerMiddlewareId);
$middleware->replaceArgument(0, new ServiceLocatorArgument($refs));
$middleware = $container->findDefinition($this->handlingMiddlewareId);
$middleware->replaceArgument(0, new ServiceLocatorArgument($handlers));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ trait PriorityTaggedServiceTrait
* @see https://bugs.php.net/53710
* @see https://bugs.php.net/60926
*
* @return Reference[]|Reference[][]
* @return array<array-key, array<Reference>>
*/
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container, array $exclude = [], bool $allowMultiple = false): array
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container, array $exclude = []): array
{
$indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null;

Expand Down Expand Up @@ -108,14 +108,8 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam
$reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $index);
}

if (null === $index) {
$refs[] = $reference;
} elseif ($allowMultiple) {
/* @psalm-suppress UndefinedMethod */
$refs[$index][] = $reference;
} else {
$refs[$index] = $reference;
}
/* @psalm-suppress UndefinedMethod */
$refs[$index][] = $reference;
}

return $refs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,70 @@

use OpenSolid\Bus\Bridge\Symfony\DependencyInjection\CompilerPass\MessageHandlersLocatorPass;
use OpenSolid\Bus\Bridge\Symfony\DependencyInjection\Configurator\MessageHandlerConfigurator;
use OpenSolid\Bus\Envelope\Envelope;
use OpenSolid\Bus\Handler\MessageHandlersCountPolicy;
use OpenSolid\Bus\Middleware\HandlingMiddleware;
use OpenSolid\Bus\Middleware\Middleware;
use OpenSolid\Bus\Middleware\NoneMiddleware;
use OpenSolid\Tests\Bus\Fixtures\AsMessageHandler;
use OpenSolid\Tests\Bus\Fixtures\MyMessage;
use OpenSolid\Tests\Bus\Fixtures\MyMessageHandler;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Compiler\AttributeAutoconfigurationPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\ServiceLocator;

class MessageHandlersLocatorPassTest extends TestCase
{
public function testProcess(): void
public function testMultipleMessageHandlingProcess(): void
{
$container = new ContainerBuilder();
$container->register('cqs.message.handle_middleware', HandlingMiddleware::class)
$this->configureContainer($container, allowMultiple: true);
$container->compile();

$envelope = Envelope::wrap(new MyMessage());

/** @var Middleware $middleware */
$middleware = $container->get('handling_middleware');
$middleware->handle($envelope, new NoneMiddleware());

$this->assertIsArray($envelope->unwrap());
$this->assertCount(2, $envelope->unwrap());
}

public function testInvalidSingleMessageHandlingProcess(): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Only one handler is allowed for message of type "OpenSolid\Tests\Bus\Fixtures\MyMessage".');

$container = new ContainerBuilder();
$this->configureContainer($container, allowMultiple: false);
$container->compile();
}

private function configureContainer(ContainerBuilder $container, bool $allowMultiple): void
{
$container->register('handling_middleware', HandlingMiddleware::class)
->setPublic(true)
->setArguments([
new AbstractArgument('cqs.message.handlers_locator'),
MessageHandlersCountPolicy::MULTIPLE_HANDLERS,
new AbstractArgument('message_handlers_locator'),
$allowMultiple ? MessageHandlersCountPolicy::MULTIPLE_HANDLERS : MessageHandlersCountPolicy::SINGLE_HANDLER,
]);

$container->register(MyMessageHandler::class)
->addTag('cqs.message.handler', ['class' => MyMessage::class]);
$container->register('handler_1', MyMessageHandler::class)
->addTag('message_handler', ['class' => MyMessage::class]);

MessageHandlerConfigurator::configure($container, AsMessageHandler::class, 'cqs.message.handler');
$container->register('handler_2', MyMessageHandler::class)
->addTag('message_handler', ['class' => MyMessage::class]);

$container->addCompilerPass(new AttributeAutoconfigurationPass());
$container->addCompilerPass(new MessageHandlersLocatorPass('cqs.message.handler', 'cqs.message.handle_middleware'));
$container->compile();
MessageHandlerConfigurator::configure($container, AsMessageHandler::class, 'message_handler');

$this->assertTrue($container->has('cqs.message.handle_middleware'));
$container->addCompilerPass(new AttributeAutoconfigurationPass());
$container->addCompilerPass(new MessageHandlersLocatorPass('message_handler', 'handling_middleware', [], $allowMultiple));
}
}

0 comments on commit bb08c97

Please sign in to comment.