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
20 changes: 20 additions & 0 deletions packages/asynchronous-bundle/src/Attribute/AsyncCommandHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace SimpleBus\AsynchronousBundle\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class AsyncCommandHandler
{
public ?string $handles;
public ?string $method;

public function __construct(?string $handles = null, ?string $method = null)
{
$this->handles = $handles;
$this->method = $method;
}
}
20 changes: 20 additions & 0 deletions packages/asynchronous-bundle/src/Attribute/AsyncEventListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace SimpleBus\AsynchronousBundle\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class AsyncEventListener
{
public ?string $subscribesTo;
public ?string $method;

public function __construct(?string $subscribesTo = null, ?string $method = null)
{
$this->subscribesTo = $subscribesTo;
$this->method = $method;
}
}
23 changes: 23 additions & 0 deletions packages/asynchronous-bundle/src/SimpleBusAsynchronousBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

namespace SimpleBus\AsynchronousBundle;

use Reflector;
use SimpleBus\AsynchronousBundle\Attribute\AsyncCommandHandler;
use SimpleBus\AsynchronousBundle\Attribute\AsyncEventListener;
use SimpleBus\AsynchronousBundle\DependencyInjection\Compiler\CollectAsynchronousEventNames;
use SimpleBus\AsynchronousBundle\DependencyInjection\SimpleBusAsynchronousExtension;
use SimpleBus\SymfonyBridge\DependencyInjection\AttributeTagResolver;
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AutoRegister;
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares;
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterHandlers;
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterSubscribers;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
Expand All @@ -21,6 +26,24 @@ public function getContainerExtension(): SimpleBusAsynchronousExtension

public function build(ContainerBuilder $container): void
{
$container->registerAttributeForAutoconfiguration(
AsyncCommandHandler::class,
static function (ChildDefinition $definition, AsyncCommandHandler $attribute, Reflector $reflector): void {
foreach (AttributeTagResolver::resolveTags($reflector, $attribute->handles, $attribute->method, 'handles') as $tag) {
$definition->addTag('asynchronous_command_handler', $tag);
}
}
);

$container->registerAttributeForAutoconfiguration(
AsyncEventListener::class,
static function (ChildDefinition $definition, AsyncEventListener $attribute, Reflector $reflector): void {
foreach (AttributeTagResolver::resolveTags($reflector, $attribute->subscribesTo, $attribute->method, 'subscribes_to') as $tag) {
$definition->addTag('asynchronous_event_subscriber', $tag);
}
}
);

$container->addCompilerPass(
new ConfigureMiddlewares('simple_bus.asynchronous.command_bus', 'asynchronous_command_bus_middleware')
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommand {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandA {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandB {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandC {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandD {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandE {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrAsyncCommandF {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

final class AttrEvent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

use SimpleBus\AsynchronousBundle\Attribute\AsyncCommandHandler;
use SimpleBus\AsynchronousBundle\Tests\Functional\Spy;

#[AsyncCommandHandler(method: 'handle')]
final class AttributeAsyncCommandHandler
{
public function __construct(private Spy $spy) {}

public function handle(AttrAsyncCommand $command): void
{
$this->spy->handled[] = $command;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

use SimpleBus\AsynchronousBundle\Attribute\AsyncEventListener;
use SimpleBus\AsynchronousBundle\Tests\Functional\Spy;

#[AsyncEventListener]
final class AttributeAsyncEventSubscriber
{
public function __construct(private Spy $spy) {}

public function __invoke(AttrEvent $event): void
{
$this->spy->handled[] = $event;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

use SimpleBus\AsynchronousBundle\Attribute\AsyncCommandHandler;
use SimpleBus\AsynchronousBundle\Tests\Functional\Spy;

#[AsyncCommandHandler(method: 'handle')]
final class AttributeUnionAsyncCommandHandler
{
public function __construct(private Spy $spy) {}

public function handle(AttrAsyncCommandA|AttrAsyncCommandB $command): void
{
$this->spy->handled[] = $command;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

use SimpleBus\AsynchronousBundle\Attribute\AsyncCommandHandler;
use SimpleBus\AsynchronousBundle\Tests\Functional\Spy;

#[AsyncCommandHandler(handles: AttrAsyncCommandE::class, method: 'handle')]
final class AttributeUnionAsyncCommandHandlerExplicit
{
public function __construct(private Spy $spy) {}

public function handle(AttrAsyncCommandE|AttrAsyncCommandF $command): void
{
$this->spy->handled[] = $command;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SimpleBus\AsynchronousBundle\Tests\Functional\Attribute;

use SimpleBus\AsynchronousBundle\Attribute\AsyncCommandHandler;
use SimpleBus\AsynchronousBundle\Tests\Functional\Spy;

#[AsyncCommandHandler(method: 'process')]
final class AttributeUnionAsyncCommandHandlerProcess
{
public function __construct(private Spy $spy) {}

public function process(AttrAsyncCommandC|AttrAsyncCommandD $command): void
{
$this->spy->handled[] = $command;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
namespace SimpleBus\AsynchronousBundle\Tests\Functional;

use PHPUnit\Framework\Attributes\Test;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommand;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandA;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandB;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandC;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandD;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandE;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrAsyncCommandF;
use SimpleBus\AsynchronousBundle\Tests\Functional\Attribute\AttrEvent;
use SimpleBus\Message\Bus\MessageBus;
use SimpleBus\Serialization\Envelope\DefaultEnvelope;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
Expand Down Expand Up @@ -37,6 +45,121 @@ public function itNotifiesSynchronousEventSubscribersAndPublishesEvents(): void
$this->assertSame([$event], $eventPublisher->publishedMessages());
}

#[Test]
public function itNotifiesAsynchronousEventSubscribersUsingAttributes(): void
{
$kernel = static::createKernel();
$kernel->boot();

$event = new AttrEvent();

/** @var MessageBus $asynchronousEventBus */
$asynchronousEventBus = $kernel->getContainer()->get('asynchronous_event_bus');
$asynchronousEventBus->handle($event);

/** @var Spy $spy */
$spy = $kernel->getContainer()->get('spy');
$this->assertSame([$event], $spy->handled);

/** @var PublisherSpy $eventPublisher */
$eventPublisher = $kernel->getContainer()->get('event_publisher_spy');
$this->assertSame([], $eventPublisher->publishedMessages());
}

#[Test]
public function itHandlesAsynchronousCommandsUsingAttributes(): void
{
$kernel = static::createKernel();
$kernel->boot();

$command = new AttrAsyncCommand();

/** @var MessageBus $asynchronousCommandBus */
$asynchronousCommandBus = $kernel->getContainer()->get('asynchronous_command_bus');
$asynchronousCommandBus->handle($command);

/** @var PublisherSpy $commandPublisher */
$commandPublisher = $kernel->getContainer()->get('command_publisher_spy');
$this->assertSame([], $commandPublisher->publishedMessages());

/** @var Spy $spy */
$spy = $kernel->getContainer()->get('spy');
$this->assertSame([$command], $spy->handled);
}

#[Test]
public function itHandlesUnionTypedCommandsInferredFromHandleMethodOnClassAttribute(): void
{
$kernel = static::createKernel();
$kernel->boot();

$a = new AttrAsyncCommandA();
$b = new AttrAsyncCommandB();

/** @var MessageBus $asynchronousCommandBus */
$asynchronousCommandBus = $kernel->getContainer()->get('asynchronous_command_bus');
$asynchronousCommandBus->handle($a);
$asynchronousCommandBus->handle($b);

/** @var PublisherSpy $commandPublisher */
$commandPublisher = $kernel->getContainer()->get('command_publisher_spy');
$this->assertSame([], $commandPublisher->publishedMessages());

/** @var Spy $spy */
$spy = $kernel->getContainer()->get('spy');
$this->assertSame([$a, $b], $spy->handled);
}

#[Test]
public function itHandlesUnionTypedCommandsWhenMethodIsSpecifiedOnClassAttribute(): void
{
$kernel = static::createKernel();
$kernel->boot();

$c = new AttrAsyncCommandC();
$d = new AttrAsyncCommandD();

/** @var MessageBus $asynchronousCommandBus */
$asynchronousCommandBus = $kernel->getContainer()->get('asynchronous_command_bus');
$asynchronousCommandBus->handle($c);
$asynchronousCommandBus->handle($d);

/** @var PublisherSpy $commandPublisher */
$commandPublisher = $kernel->getContainer()->get('command_publisher_spy');
$this->assertSame([], $commandPublisher->publishedMessages());

/** @var Spy $spy */
$spy = $kernel->getContainer()->get('spy');
$this->assertSame([$c, $d], $spy->handled);
}

#[Test]
public function itHonorsExplicitHandlesOnClassAttributeEvenWithUnionTypedMethod(): void
{
$kernel = static::createKernel();
$kernel->boot();

$e = new AttrAsyncCommandE();
$f = new AttrAsyncCommandF();

/** @var MessageBus $asynchronousCommandBus */
$asynchronousCommandBus = $kernel->getContainer()->get('asynchronous_command_bus');
$asynchronousCommandBus->handle($e);

// Send unhandled command through the synchronous command bus to be published
/** @var MessageBus $commandBus */
$commandBus = $kernel->getContainer()->get('command_bus');
$commandBus->handle($f);

/** @var Spy $spy */
$spy = $kernel->getContainer()->get('spy');
$this->assertSame([$e], $spy->handled, 'Only explicitly handled message should be handled');

/** @var PublisherSpy $commandPublisher */
$commandPublisher = $kernel->getContainer()->get('command_publisher_spy');
$this->assertSame([$f], $commandPublisher->publishedMessages(), 'Unhandled union branch should be published');
}

#[Test]
public function itNotifiesAsynchronousEventSubscribers(): void
{
Expand Down
Loading