Skip to content

Commit

Permalink
Merge pull request #16 from sabbelasichon/add-event-middleware
Browse files Browse the repository at this point in the history
FEATURE: Add EventMiddleware
  • Loading branch information
sabbelasichon authored Nov 4, 2022
2 parents bd635e0 + c64f589 commit 7d8d87d
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 1 deletion.
19 changes: 19 additions & 0 deletions Classes/Middleware/Event/CommandEventInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Middleware\Event;

interface CommandEventInterface
{
public function getCommand(): object;

public function getName(): string;
}
52 changes: 52 additions & 0 deletions Classes/Middleware/Event/CommandFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Middleware\Event;

final class CommandFailed implements CommandEventInterface
{
private object $command;

private \Exception $exception;

private bool $exceptionCaught = false;

public function __construct(object $command, \Exception $exception)
{
$this->command = $command;
$this->exception = $exception;
}

public function getException(): \Exception
{
return $this->exception;
}

public function catchException(): void
{
$this->exceptionCaught = true;
}

public function getCommand(): object
{
return $this->command;
}

public function getName(): string
{
return 'command.failed';
}

public function isExceptionCaught(): bool
{
return $this->exceptionCaught;
}
}
32 changes: 32 additions & 0 deletions Classes/Middleware/Event/CommandHandled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Middleware\Event;

final class CommandHandled implements CommandEventInterface
{
private object $command;

public function __construct(object $command)
{
$this->command = $command;
}

public function getCommand(): object
{
return $this->command;
}

public function getName(): string
{
return 'command.received';
}
}
32 changes: 32 additions & 0 deletions Classes/Middleware/Event/CommandReceived.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Middleware\Event;

final class CommandReceived implements CommandEventInterface
{
private object $command;

public function __construct(object $command)
{
$this->command = $command;
}

public function getCommand(): object
{
return $this->command;
}

public function getName(): string
{
return 'command.handled';
}
}
50 changes: 50 additions & 0 deletions Classes/Middleware/EventMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Middleware;

use League\Tactician\Middleware;
use Psr\EventDispatcher\EventDispatcherInterface;
use Ssch\T3Tactician\Middleware\Event\CommandFailed;
use Ssch\T3Tactician\Middleware\Event\CommandHandled;
use Ssch\T3Tactician\Middleware\Event\CommandReceived;

final class EventMiddleware implements Middleware
{
private EventDispatcherInterface $eventDispatcher;

public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}

public function execute($command, callable $next)
{
try {
$this->eventDispatcher->dispatch(new CommandReceived($command));

$returnValue = $next($command);

$this->eventDispatcher->dispatch(new CommandHandled($command));

return $returnValue;
} catch (\Exception $e) {
$event = new CommandFailed($command, $e);
$this->eventDispatcher->dispatch($event);

if (! $event->isExceptionCaught()) {
throw $e;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use League\Tactician\Handler\CommandHandlerMiddleware;
use League\Tactician\Handler\MethodNameInflector\ClassNameInflector;
use League\Tactician\Handler\MethodNameInflector\InvokeInflector;
use Ssch\T3Tactician\Middleware\EventMiddleware;
use Ssch\T3Tactician\Middleware\ValidatorMiddleware;

return [
Expand All @@ -22,7 +23,11 @@
],
'foo' => [
'method_inflector' => ClassNameInflector::class,
'middleware' => [ValidatorMiddleware::class, 'tactician.commandbus.foo.middleware.command_handler'],
'middleware' => [
EventMiddleware::class,
ValidatorMiddleware::class,
'tactician.commandbus.foo.middleware.command_handler',
],
],
'bar' => [
'middleware' => [ValidatorMiddleware::class, CommandHandlerMiddleware::class],
Expand Down
97 changes: 97 additions & 0 deletions Tests/Unit/Middleware/EventMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

/*
* This file is part of the "t3_tactician" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/

namespace Ssch\T3Tactician\Tests\Unit\Middleware;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\EventDispatcher\EventDispatcherInterface;
use Ssch\T3Tactician\Middleware\Event\CommandFailed;
use Ssch\T3Tactician\Middleware\EventMiddleware;
use Ssch\T3Tactician\Tests\Unit\Fixtures\FakeCommand;

final class EventMiddlewareTest extends TestCase
{
use ProphecyTrait;

public function testThatEventsAreCalledSuccessfully(): void
{
// Arrange
$eventDispatcher = new class() implements EventDispatcherInterface {
public array $firedEvents = [];

public function dispatch(object $event): object
{
$this->firedEvents[] = $event;

return $event;
}
};

$subject = new EventMiddleware($eventDispatcher);

// Act
$subject->execute(new FakeCommand(), function () {
});

// Assert
self::assertCount(2, $eventDispatcher->firedEvents);
}

public function testThatFailingCommandFiresEventButThrowsExceptionInTheEnd(): void
{
// Assert
$this->expectException(\Exception::class);

// Arrange
$eventDispatcher = new class() implements EventDispatcherInterface {
public function dispatch(object $event): object
{
return $event;
}
};

$subject = new EventMiddleware($eventDispatcher);

// Act
$subject->execute(new FakeCommand(), function () {
throw new \Exception('Some deep failure is thrown in your application');
});
}

public function testThatFailingCommandFiresEventAndCatchesException(): void
{
$eventDispatcher = new class() implements EventDispatcherInterface {
public array $firedEvents = [];

public function dispatch(object $event): object
{
if ($event instanceof CommandFailed) {
$event->catchException();
}

$this->firedEvents[] = $event;

return $event;
}
};

$subject = new EventMiddleware($eventDispatcher);

// Act
$subject->execute(new FakeCommand(), function () {
throw new \Exception('Some deep failure is thrown in your application');
});

// Assert
self::assertCount(2, $eventDispatcher->firedEvents);
}
}

0 comments on commit 7d8d87d

Please sign in to comment.