diff --git a/.gitignore b/.gitignore index 0160f76..ad80ef3 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ nbproject .buildpath .DS_Store .idea +.php_cs.cache .project .settings vendor diff --git a/.php_cs b/.php_cs index cbad790..363296d 100644 --- a/.php_cs +++ b/.php_cs @@ -1,40 +1,10 @@ in('src') - ->in('config') - ->in('tests') - ->in('examples'); -$config = Symfony\CS\Config\Config::create(); -$config->level(null); -$config->fixers( - array( - 'braces', - 'duplicate_semicolon', - 'elseif', - 'empty_return', - 'encoding', - 'eof_ending', - 'function_call_space', - 'function_declaration', - 'indentation', - 'join_function', - 'line_after_namespace', - 'linefeed', - 'lowercase_keywords', - 'parenthesis', - 'multiple_use', - 'method_argument_space', - 'object_operator', - 'php_closing_tag', - 'remove_lines_between_uses', - 'short_array_syntax', - 'short_tag', - 'standardize_not_equal', - 'trailing_spaces', - 'unused_use', - 'visibility', - 'whitespacy_lines', - ) -); -$config->finder($finder); + +$config = new Prooph\CS\Config\Prooph(); +$config->getFinder()->in(__DIR__); + +$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__; + +$config->setCacheFile($cacheDir . '/.php_cs.cache'); + return $config; diff --git a/.travis.yml b/.travis.yml index 1d68272..dff9b63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,35 +3,31 @@ language: php matrix: fast_finish: true include: - - php: 5.5 - env: - - DEPENDENCIES="" - - php: 5.5 - env: - - DEPENDENCIES="--prefer-lowest --prefer-stable" - - php: 5.6 - env: - - DEPENDENCIES="" - - php: 5.6 - env: - - DEPENDENCIES="--prefer-lowest --prefer-stable" - - php: 7 + - php: 7.1 env: - DEPENDENCIES="" + - EXECUTE_CS_CHECK=true - TEST_COVERAGE=true - - php: 7 + - php: 7.1 env: - DEPENDENCIES="--prefer-lowest --prefer-stable" +cache: + directories: + - $HOME/.composer/cache + - $HOME/.php-cs-fixer + - $HOME/.local + before_script: + - mkdir -p "$HOME/.php-cs-fixer" - phpenv config-rm xdebug.ini - composer self-update - composer update --prefer-dist $DEPENDENCIES script: - - php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml - - ./vendor/bin/php-cs-fixer fix -v --diff --dry-run - - ./vendor/bin/docheader check config/ examples/ src/ tests/ + - if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/docheader check src/ tests/; fi after_success: - if [[ $TEST_COVERAGE == 'true' ]]; then php vendor/bin/coveralls -v; fi diff --git a/LICENSE.txt b/LICENSE.txt index 0f30df1..33a1ab3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,27 +1,28 @@ -Copyright (c) 2013, Alexander Miertsch contact@prooph.de +Copyright (c) 2013-2017, prooph software GmbH +Copyright (c) 2015-2017, Sascha-Oliver Prolic All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - * Neither the name of Alexander Miertsch nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. +* Neither the name of the prooph software GmbH nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index df6b883..48ebeda 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PSB - ProophServiceBus -PHP 5.5+ lightweight message bus supporting CQRS and Micro Services +PHP 7.1+ lightweight message bus supporting CQRS and Micro Services [![Build Status](https://travis-ci.org/prooph/service-bus.png?branch=master)](https://travis-ci.org/prooph/service-bus) [![Coverage Status](https://coveralls.io/repos/prooph/service-bus/badge.svg?branch=master&service=github)](https://coveralls.io/github/prooph/service-bus?branch=master) @@ -45,12 +45,12 @@ $router = new CommandRouter(); //Register a callback as CommandHandler for the EchoText command $router->route('Prooph\ServiceBus\Example\Command\EchoText') - ->to(function (EchoText $aCommand) { + ->to(function (EchoText $aCommand): void { echo $aCommand->getText(); }); //Expand command bus with the router plugin -$commandBus->utilize($router); +$router->attachToMessageBus($commandBus); //We create a new Command $echoText = new EchoText('It works'); diff --git a/composer.json b/composer.json index 3371cde..a9a8d9e 100644 --- a/composer.json +++ b/composer.json @@ -24,30 +24,30 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": "~5.5 || ~7.0", - "beberlei/assert": "^2.4", - "prooph/common" : "^3.7" + "php": "^7.1", + "prooph/common" : "^4.0.0" }, "require-dev": { - "react/promise" : "^2.2.2", - "phpunit/phpunit": "^4.8.23", - "fabpot/php-cs-fixer": "^1.7", + "react/promise" : "^2.4.1", + "phpunit/phpunit": "^6.0", + "phpspec/prophecy": "dev-patch-1 as 1.6.2", + "prooph/php-cs-fixer-config": "^0.1.1", "satooshi/php-coveralls": "^1.0", - "container-interop/container-interop" : "^1.1", - "sandrokeil/interop-config": "^1.0", - "tobiju/bookdown-bootswatch-templates": "^1.0", - "malukenho/docheader": "^0.1.3" + "psr/container": "^1.0", + "sandrokeil/interop-config": "^2.0.1", + "prooph/bookdown-template": "^0.2.3", + "malukenho/docheader": "^0.1.4" }, "suggest": { - "react/promise": "^2.2.2 for usage with provided QueryBus", - "prooph/event-store": "Let the EventBus dispatch persisted DomainEvents", + "react/promise": "^2.4.1 for usage with provided QueryBus", + "prooph/event-store-bus-bridge": "Let the EventBus dispatch persisted DomainEvents", "zendframework/zend-servicemanager": "Use Zend ServiceManager to lazy load your handlers and listeners", "prooph/service-bus-zfc-rbac-bridge": "Use ZfcRbac as authorization service for route and finalize guard", - "container-interop/container-interop": "For usage of provided factories", + "psr/container": "^1.0 for usage of provided factories", "sandrokeil/interop-config": "For usage of provided factories" }, "conflict": { - "sandrokeil/interop-config": "<1.0" + "sandrokeil/interop-config": "<2.0.1" }, "autoload": { "psr-4": { @@ -59,6 +59,11 @@ "ProophTest\\ServiceBus\\": "tests/" } }, + "extra": { + "branch-alias": { + "dev-develop": "6.0-dev" + } + }, "scripts": { "check": [ "@cs", @@ -67,5 +72,11 @@ "cs": "php-cs-fixer fix -v --diff --dry-run", "cs-fix": "php-cs-fixer fix -v --diff", "test": "phpunit" - } + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/prolic/prophecy.git" + } + ] } diff --git a/config/prooph_service_bus.config.php b/config/prooph_service_bus.config.php index 5eddaa2..4e52412 100644 --- a/config/prooph_service_bus.config.php +++ b/config/prooph_service_bus.config.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + /** * This file contains default configuration for prooph/service-bus * It is meant to be used together with at least one of the container-aware factories @@ -40,7 +42,7 @@ //the factory will pull the producer from the container and set up an AsyncSwitchMessageRouter //using the producer AND decorating the actual configured router //'async_switch' => 'container_id_of_async_message_producer', - ] + ], ], //This section will be used by Prooph\ServiceBus\Container\EventBusFactory 'event_bus' => [ @@ -61,7 +63,7 @@ //the factory will pull the producer from the container and set up an AsyncSwitchMessageRouter //using the producer AND decorating the actual configured router //'async_switch' => 'container_id_of_async_message_producer', - ] + ], ], //This section will be used by Prooph\ServiceBus\Container\QueryBusFactory 'query_bus' => [ @@ -82,7 +84,7 @@ //the factory will pull the producer from the container and set up an AsyncSwitchMessageRouter //using the producer AND decorating the actual configured router //'async_switch' => 'container_id_of_async_message_producer', - ] + ], ], ], //EO service_bus ], //EO prooph diff --git a/config/services.php b/config/services.php index 7ee6622..09a0396 100644 --- a/config/services.php +++ b/config/services.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus; return [ @@ -21,11 +23,11 @@ // to expose message name in UnauthorizedException Plugin\Guard\RouteGuard::class => [ Container\Plugin\Guard\RouteGuardFactory::class, - 'exposeMessageName' + 'exposeMessageName', ], Plugin\Guard\FinalizeGuard::class => [ Container\Plugin\Guard\FinalizeGuardFactory::class, - 'exposeMessageName' + 'exposeMessageName', ], - ] + ], ]; diff --git a/docs/async_message_producer.md b/docs/async_message_producer.md index e1774c8..22ca87f 100644 --- a/docs/async_message_producer.md +++ b/docs/async_message_producer.md @@ -1,61 +1,66 @@ -# Async Message Producer - -Messaging becomes really interesting if you process your messages asynchronous. PSB can -hide such an asynchronous workflow behind a unified interface. You can start with synchronous message dispatching by -routing your messages directly to message handlers and if you later want to improve response times you can switch to -async processing on a per message basis by routing the appropriate messages to a message producer which hands them over to -a messaging system like rabbitMQ, zeroMQ, gearman or beanstalkd. - -## Available MessageProducer - -- [BernardProducer](https://github.com/prooph/psb-bernard-producer): Queue multi-backend providing different - drivers like Doctrine DBAL and Predis (see [https://github.com/bernardphp/bernard](https://github.com/bernardphp/bernard) for a complete list of drivers) -- [GuzzleHttpProducer](https://github.com/prooph/psb-http-producer): Send messages to a remote system using - HTTP -- [ZeromqProducer](https://github.com/prooph/psb-zeromq-producer): Async message handling using super fast and simple to -set up ZeroMQ - -## Usage - -If you want to set up a bus that handles all messages async you can do so by attaching a `Prooph\ServiceBus\Plugin\MessageProducerPlugin` -initialized with your message producer of choice to a message bus. - -Let's look at a simple example using the `psb-zeromq-producer` - -```php -//app bootstrap -$container = new Container; -$container['config'] = [ - 'prooph' => [ - 'zeromq_producer' => [ - 'dsn' => 'tcp://127.0.0.1:5555', // ZMQ Server Address. - 'persistent_id' => 'example', // ZMQ Persistent ID to keep connections alive between requests. - 'rpc' => false, // Use as Query Bus. - ] - ] -]; - -$factory = \Prooph\ServiceBus\Message\ZeroMQ\Container\ZeroMQMessageProducerFactory; -$zmqProducer = $factory($container); - -$commandBus = new \Prooph\ServiceBus\CommandBus(); - -$messageProducerForwarder = new \Prooph\ServiceBus\Plugin\MessageProducerPlugin($zmqProducer); - -$commandBus->utilize($messageProducerForwarder); - -$echoText = new ExampleCommand('It works'); -$commandBus->dispatch($echoText); -``` - -You can also route individual messages to message producer by using a message router plugin. - -*Note: `Prooph\ServiceBus\Plugin\Router\RegexRouter` is a good choice -if you want to handle all messages of a specific namespace async.* - -## Async Querying - -An async message producer for the QueryBus needs to provide a response by resolving the handed over `React\Promise\Deferred`. -When using a messaging system like ZeroMQ for example you can make use of request/response mode or RPC mode. -HTTP APIs provide responses naturally. -So these are both good candidates to use for remote querying. +# Async Message Producer + +Messaging becomes really interesting if you process your messages asynchronously. PSB can +hide such an asynchronous workflow behind a unified interface. You can start with synchronous message +dispatching by routing your messages directly to message handlers and if you later want to improve response +times you can switch to async processing on a per message basis by routing the appropriate messages to a +message producer which hands them over to a messaging system like RabbitMQ, ZeroMQ, Gearman, Beanstalkd or any +other queue. + +## Available MessageProducer + +- [BernardProducer](https://github.com/prooph/psb-bernard-producer): Queue multi-backend providing different + drivers like Doctrine DBAL and Predis (see + [https://github.com/bernardphp/bernard](https://github.com/bernardphp/bernard) for a complete list of drivers) +- [GuzzleHttpProducer](https://github.com/prooph/psb-http-producer): Send messages to a remote system using + HTTP +- [ZeroMQProducer](https://github.com/prooph/psb-zeromq-producer): Async message handling using super fast +and simple to set up ZeroMQ +- [HumusAmqpProducer](https://github.com/prooph/humus-amqp-producer): Async handling using amqp protocol +(f.e. with RabbitMQ). This also includes JSON-RPC features. + +## Usage + +If you want to set up a bus that handles all messages async you can do so by attaching a +`Prooph\ServiceBus\Plugin\MessageProducerPlugin` initialized with your message producer of choice +to a message bus. + +Let's look at a simple example using the `psb-zeromq-producer` + +```php +//app bootstrap +$container = new Container; +$container['config'] = [ + 'prooph' => [ + 'zeromq_producer' => [ + 'dsn' => 'tcp://127.0.0.1:5555', // ZMQ Server Address. + 'persistent_id' => 'example', // ZMQ Persistent ID to keep connections alive between requests. + 'rpc' => false, // Use as Query Bus. + ] + ] +]; + +$factory = \Prooph\ServiceBus\Message\ZeroMQ\Container\ZeroMQMessageProducerFactory; +$zmqProducer = $factory($container); + +$commandBus = new \Prooph\ServiceBus\CommandBus(); + +$messageProducerForwarder = new \Prooph\ServiceBus\Plugin\MessageProducerPlugin($zmqProducer); + +$messageProducerForwarder->attachToMessageBus($commandBus); + +$echoText = new ExampleCommand('It works'); +$commandBus->dispatch($echoText); +``` + +You can also route individual messages to message producer by using a message router plugin. + +*Note: `Prooph\ServiceBus\Plugin\Router\RegexRouter` is a good choice +if you want to handle all messages of a specific namespace async.* + +## Async Querying + +An async message producer for the QueryBus needs to provide a response by resolving the handed over +`React\Promise\Deferred`. When using a messaging system like ZeroMQ for example you can make use of +request/response mode or RPC mode. HTTP APIs provide responses naturally. So these are both good +candidates to use for remote querying. diff --git a/docs/bookdown.json b/docs/bookdown.json index c87c47a..cb6883f 100644 --- a/docs/bookdown.json +++ b/docs/bookdown.json @@ -9,5 +9,5 @@ {"factories": "factories.md"} ], "target": "./html", - "template": "../vendor/tobiju/bookdown-bootswatch-templates/templates/main.php" + "template": "../vendor/prooph/bookdown-template/templates/main.php" } diff --git a/docs/factories.md b/docs/factories.md index 34bb919..8d96684 100644 --- a/docs/factories.md +++ b/docs/factories.md @@ -11,7 +11,7 @@ the message buses without the need to rely on a specific framework. However, the ### Requirements 1. Your Inversion of Control container must implement the [interop-container interface](https://github.com/container-interop/container-interop). -2. [interop-config](https://github.com/sandrokeil/interop-config) must be installed +2. [interop-config](https://github.com/sandrokeil/interop-config) must be installed 3. The application configuration should be registered with the service id `config` in the container. *Note: Don't worry, if your environment doesn't provide the requirements. You can diff --git a/docs/message_bus.md b/docs/message_bus.md index 0eb20a2..e1e1906 100644 --- a/docs/message_bus.md +++ b/docs/message_bus.md @@ -2,11 +2,13 @@ ## Commanding -When you want to apply [CQRS](http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf) the command bus is your best friend. +When you want to apply [CQRS](http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf) the command bus is +your best friend. It takes an incoming command message and routes it to the responsible command handler. -The advantage of using a CommandBus instead of calling command handlers directly is, that you can change your model without effecting -the application logic. You can work with command versions to dispatch a newer version to a new command handler and older -versions to old command handlers. Your model can support different versions at the same time which makes migrations a lot easier. +The advantage of using a CommandBus instead of calling command handlers directly is, that you can change your model +without effecting the application logic. You can work with command versions to dispatch a newer version to a new +command handler and older versions to old command handlers. Your model can support different versions at the same +time which makes migrations a lot easier. Second feature of a command bus could be automatic transaction handling. And for distributed systems it is also interesting to push the command on a queue and handle it asynchronous. @@ -14,114 +16,262 @@ And for distributed systems it is also interesting to push the command on a queu When dividing your domain logic into modules or bounded contexts you need a way to inform the outside world about events that happened in your model. -An EventBus is responsible for dispatching event messages to all interested listeners. If a listener is part of another system -the event may need to be send to a remote interface. +An EventBus is responsible for dispatching event messages to all interested listeners. If a listener is part of +another system the event may need to be send to a remote interface. ## Querying -A system based on [Microservices](http://martinfowler.com/articles/microservices.html) requires a lightweight communication channel. -The two most used protocols are HTTP request-response with resource API's and lightweight messaging. The latter is supported by prooph/service-bus -out-of-the-box but HTTP API's can be integrated too. -The QueryBus is responsible for routing a query message to a so called finder. The query indicates that the producer expects a response. -The finder's responsibility is to fetch data from a data source using the query parameters defined in the query message. It is up to the finder if the data is fetched synchronous -or asynchronous, so the QueryBus returns a `React\Promise\Promise` to the callee. +A system based on [Microservices](http://martinfowler.com/articles/microservices.html) requires a lightweight +communication channel. +The two most used protocols are HTTP request-response with resource API's and lightweight messaging. The latter +is supported by prooph/service-bus out-of-the-box but HTTP API's can be integrated too. +The QueryBus is responsible for routing a query message to a so called finder. The query indicates that the +producer expects a response. +The finder's responsibility is to fetch data from a data source using the query parameters defined in the query +message. It is up to the finder if the data is fetched synchronous or asynchronous, so the QueryBus returns +a `React\Promise\Promise` to the callee. ## API -All three bus types extend the same base class `Prooph\ServiceBus\MessageBus` and therefore make use of an event-driven message dispatch process. -Take a look at the CommandBus API. It is the same for EventBus and QueryBus except that the QueryBus returns a promise from `QueryBus::dispatch`. +All three bus types extend the same base class `Prooph\ServiceBus\MessageBus` and therefore make use of an +event-driven message dispatch process. Take a look at the CommandBus API. It is the same for EventBus and +QueryBus except that the QueryBus returns a promise from `QueryBus::dispatch`. ```php class CommandBus extends MessageBus { - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate $plugin - */ - public function utilize($plugin); + public function attach(string $eventName, callable $listener, int $priority = 0): ListenerHandler - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate $plugin - */ - public function deactivate($plugin); - - /** - * @return \Prooph\Common\Event\ActionEventEmitter - */ - public function getActionEventEmitter(); + public function detach(ListenerHandler $handler): void /** * @param mixed $command - * @throws Exception\ServiceBusException + * + * @throws CommandDispatchException */ - public function dispatch($command); + public function dispatch($command): void; } ``` -The public API of a message bus is very simple. You can attach and detach plugins which are simple event listener aggregates -and you can dispatch a message. +The public API of a message bus is very simple. You can attach and detach plugins which are simple event +listener aggregates and you can dispatch a message. ## Event-Driven Dispatch -Internally a prooph message bus uses an [event-driven process](https://github.com/prooph/common#actioneventemitter) to dispatch messages. +Internally a prooph message bus uses an [event-driven process](https://github.com/prooph/common#actioneventemitter) +to dispatch messages. This offers a lot of flexibility without the need to define interfaces for messages. -A message can be everything even a string. prooph/service-bus doesn't care. But using some defaults will reduce the -number of required plugins and increase performance. +A message can be everything even a string. prooph/service-bus doesn't care. But using some defaults will reduce +the number of required plugins and increase performance. -But first let's take a look at the internals of a message dispatch process and the differences between the bus types. +But first let's take a look at the internals of a message dispatch process and the differences between the +bus types. -### initialize +### dispatch -This action event is triggered right after `MessageBus::dispatch($message)` is invoked. At this time the action event only contains the `message`. +This action event is triggered right after `MessageBus::dispatch($message)` is invoked. -### detect-message-name (optional) +The following default priorities are integrated: + +```php +public const PRIORITY_INITIALIZE = 400000; +public const PRIORITY_DETECT_MESSAGE_NAME = 300000; +public const PRIORITY_ROUTE = 200000; +public const PRIORITY_LOCATE_HANDLER = 100000; +public const PRIORITY_INVOKE_HANDLER = 0; +``` + +#### initialize + +At this time the action event only contains the `message`. You can attach any listeners for initialization. + +#### detect-message-name Before a message handler can be located, the message bus needs to know how the message is named. Their are two -possibilities to provide the information. The message can implement the [Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) interface. -In this case the message bus picks the name directly from the message and set it as param `message-name` in the action event for later use. The `detect-message-name` event is not triggered. If the message -does not implement the interface the `detect-message-name` event is triggered and a plugin needs to inject the name using `ActionEvent::setParam('message-name', $messageName)`. +possibilities to provide the information. The message can implement the +[Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) +interface. +In this case the message bus picks the name directly from the message and set it as param `message-name` in the +action event for later use. The `detect-message-name` event is not triggered. If the message +does not implement the interface the `detect-message-name` priority can be used to add a plugin to inject the +name using `ActionEvent::setParam('message-name', $messageName)`. If no `message-name` was set by a listener the message bus uses a fallback: - FQCN of message in case of object - message => message-name in case of string - `gettype($message)` in all other cases -### route +#### route -During the `route` action event a plugin (typically a router) should provide the responsible message handler either in form of a ready to use `callable`, an object or just a string. -The latter should be a service id that can be passed to a service locator to get an instance of the message handler. -The message handler should be set as action event param `message-handler` (for CommandBus and QueryBus) or `event-listeners` (for EventBus). +During the `route` phase a plugin (typically a router) should provide the responsible message handler either +in form of a ready to use `callable`, an object or just a string. +The latter should be a service id that can be passed to a service locator to get an instance of the message +handler. +The message handler should be set as action event param `message-handler` (for CommandBus and QueryBus) or +`event-listeners` (for EventBus). -As you can see command and query bus work with a single message handler whereby the event bus works with multiple listeners. -This is one of the most important differences. Only the event bus allows multiple message handlers per message and therefor uses -a slightly different dispatch process. +As you can see command and query bus work with a single message handler whereby the event bus works with +multiple listeners. +This is one of the most important differences. Only the event bus allows multiple message handlers per +message and therefor uses a slightly different dispatch process. -### locate-handler (optional) +#### locate-handler -After routing the message, the message bus checks if the handler was provided as a string. If so it triggers the -`locate-handler` action event. This is the latest time to provide an object or callable as message handler. If no plugin was able to provide one the message bus throws an exception. +After routing the message, the message bus checks if the handler was provided as a string. This is the +latest time to provide an object or callable as message handler. If no plugin was able to provide one the +message bus throws an exception. -### invoke-handler / invoke-finder (optional) +#### invoke-handler -Having the message handler in place it's time to invoke it with the message. `callable` message handlers are invoked by the bus. However, the `invoke-handler` / `invoke-finder` events are always triggered. +Having the message handler in place it's time to invoke it with the message. `callable` message handlers +are invoked by the bus. However, the `invoke-handler` / `invoke-finder` events are always triggered. At this stage all three bus types behave a bit different. - CommandBus: invokes the handler with the command message. A `invoke-handler` event is triggered. -- QueryBus: much the same as the command bus but the message handler is invoked with the query message and a `React\Promise\Deferred` -that needs to be resolved by the message handler aka finder. The query bus triggers a `invoke-finder` action event to indicate -that a finder should be invoked and not a normal message handler. -- EventBus: loops over all `event-listeners` and triggers the `locate-handler` and `invoke-handler` action events for each message listener. - -*Note: * The command and query bus have a mechanism to check if the command or query was handled. If not they throw an exception. +- QueryBus: much the same as the command bus but the message handler is invoked with the query message +and a `React\Promise\Deferred` that needs to be resolved by the message handler aka finder. The query bus +triggers a `invoke-finder` action event to indicate that a finder should be invoked and not a normal message +handler. +- EventBus: loops over all `event-listeners` and triggers the `locate-handler` and `invoke-handler` action +events for each message listener. + +*Note: * The command and query bus have a mechanism to check if the command or query was handled. If not they +throw an exception. The event bus does not have such a mechanism as having no listener for an event is a valid case. -### handle-error +### finalize + +This action event is always triggered at the end of the process no matter if the process was successful +or an exception was thrown. It is the ideal place to attach a monitoring plugin. -If at any time a plugin or the message bus itself throws an exception it is caught and passed as param `exception` to the action event. The normal action event chain breaks and a -`handle-error` event is triggered instead. Plugins can then access the exception by getting it from the action event. -A `handle-error` plugin or a `finalize` plugin can unset the exception by calling `ActionEvent::setParam("exception", null)`. -When all plugins are informed about the error and no one has unset the exception the message bus throws a Prooph\ServiceBus\Exception\MessageDispatchException to inform the outside world about the error. +If at any time a plugin or the message bus itself throws an exception it is caught and passed as param +`exception` to this action event. The normal action event chain breaks and a `finalize` event is triggered +instead. Plugins can then access the exception by getting it from the action event. +A `finalize` plugin can unset the exception by calling `ActionEvent::setParam("exception", null)`. +When all plugins are informed about the error and no one has unset the exception the message bus +throws a Prooph\ServiceBus\Exception\MessageDispatchException to inform the outside world about the error. -### finalize +Note: The query bus has another additional priority `PRIORITY_PROMISE_REJECT` which is used to reject the promise +in case of an exception during the finalize event. If you want to remove the exception with a listener, you need +to add your listener with a higher priority than that. + +## Migration from v5 + +### Events & Priorities + +Two things are to consider, when upgrading from v5. + +1) The `handle-error` event is gone. If you want to have a plugin that tracks exception, you need to use the +`finalize` event and check for existence of an exception. This can look like this: + +```php +$commandBus->attach( + CommandBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) { + if ($ex = $actionEvent->getParam(CommandBus::EVENT_PARAM_EXCEPTION) { + // do something + } + } +); +``` + +2) There is a new `dispatch` event replacing all other previously existing events. It is controlled by +event priorities instead. So if your previous plugin looked like this: + +```php +$commandBus->attach( + CommandBus::EVENT_INVOKE_HANDLER, + function (ActionEvent $actionEvent) { + if ($ex = $actionEvent->getParam(CommandBus::EVENT_PARAM_EXCEPTION) { + // do something + } + } +); +``` + +it now has to look like this: + +```php +$commandBus->attach( + CommandBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) { + if ($ex = $actionEvent->getParam(CommandBus::EVENT_PARAM_EXCEPTION) { + // do something + } + }, + CommandBus::PRIORITY_INVOKE_HANDLER +); +``` + +3) Attaching to ActionEvents + +Instead of calling: + +```php +$commandBus + ->getActionEventEmitter() + ->attachListener(string $event, callable $listener, int $priority = 1): ListenerHandler; +``` + +It's more simple now: + +```php +$commandBus->attach(string $event, callable $listener, int $priority = 1): ListenerHandler; +``` + +4) Plugins + +Instead of implementing `Prooph\Common\Event\ActionEventListenerAggregate` a plugin now has to +implement `Prooph\ServiceBus\Plugin\Plugin`. The signature is: + +```php +public function attachToMessageBus(MessageBus $messageBus): void; + +public function detachFromMessageBus(MessageBus $messageBus): void; +``` + +### Further changes + +#### FinderInvokeStrategy + +Instead of having this: + +```php +$finder->findQueryOne(QueryOne $query, Deferred $deferred = null): void; +``` + +you simply have this: + +```php +$finder->find(QueryOne $query, Deferred $deferred = null): void; +``` + +If you want to go back to the old behaviour, you can do the following things: + +a) + +```php +class MyFinder +{ + public function find(Query $query, Deferred $deferred = null): void + { + if ($query instanceof QueryOne) { + $this->findQueryOne($query, $deferred); + } elseif ($query instanceof QueryTwo) { + $this->findQueryTwo($query, $deferred); + } else { + throw new \InvalidArgumentException('Unknown query passed'); + } + } +} +``` + +or b) Write a custom FinderInvokeStrategy. + +#### HandleCommandStrategy + +Same as for FinderInvokeStrategy, the handler is only expected have a `handle(Command $command): void` method. +If you need the old behaviour back, implement this in your handlers or write a custom plugin. -This action event is always triggered at the end of the process no matter if the process was successful or an exception was thrown. It is the ideal place to -attach a monitoring plugin. +#### OnEventStrategy +Same as above: There handler is only expected to have a `onEvent(Event $message): void` method. diff --git a/docs/plugins.md b/docs/plugins.md index 55b12ed..a17fad4 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -23,7 +23,7 @@ $router->route('My.Command.SendPaymentEmail')->to(array($mailer, "handleSendPaym $router->route('My.Command.PayOrder')->to("payment_processor"); //Add the router to a CommandBus -$commandBus->utilize($router); +$router->attachToMessageBus($commandBus); ``` ### Prooph\ServiceBus\Plugin\Router\QueryRouter @@ -48,7 +48,7 @@ $router->route('My.Event.ArticleWasBought')->to(new OrderCartUpdater())->andTo(n $router->route('My.Event.OrderWasPayed')->to("delivery_processor"); //Add the router to an EventBus -$eventBus->utilize($router); +$router->attachToMessageBus($eventBus); ``` ### Prooph\ServiceBus\Plugin\Router\RegexRouter @@ -66,7 +66,7 @@ $router = new RegexRouter(array('/^My\.Command\.Buy.*/' => new BuyArticleHandler $router->route('/^My\.Command\.Register.*/')->to(new RegisterUserHandler()); //Add the router to a CommandBus -$commandBus->utilize($router); +$router->attachToMessageBus($commandBus); //When routing an event you can provide a list of listeners for each pattern ... $router = new RegexRouter(array('/^My\.Event\.Article.*/' => array(new OrderCartUpdater(), new InventoryUpdater()))); @@ -80,7 +80,7 @@ $router->route('/^My\.Event\.Article.*/')->to(new OrderCartUpdater()); $router->route('/^My\.Event\.Article.*/')->to(new InventoryUpdater()); //Add the router to an EventBus -$eventBus->utilize($router); +$router->attachToMessageBus($eventBus); ``` ### Prooph\ServiceBus\Plugin\Router\AsyncSwitchMessageRouter @@ -103,18 +103,16 @@ $router = new AsyncSwitchMessageRouter( $myRouter, $asyncMessageProducer ); - + //Add the router to a CommandBus -$commandBus->utilize($router); +$router->attachToMessageBus($commandBus); ``` ## Invoke Strategies An invoke strategy knows how a message handler can be invoked. You can register many invoke strategies at once depending on how many different handler types you are using. The best way is to choose a convention and go with it. PSB ships with the invoke strategies -listed below. If your favorite convention is not there you can easily write your own invoke strategy -by extending `Prooph\ServiceBus\Plugin\InvokeStrategy\AbstractInvokeStrategy` and implementing the -`canInvoke` and `invoke` methods. +listed below. ### Available Strategies @@ -162,14 +160,14 @@ $serviceManager = new ServiceManager(new Config([ ])); //The ZF2\ServiceManager implements Interop\Container\ContainerInterface since v2.6 -$commandBus->utilize(new ServiceLocatorPlugin($serviceManager)); +(new ServiceLocatorPlugin($serviceManager))->attachToMessageBus($commandBus); $router = new CommandRouter(); //In the routing map we use the alias of the command handler $router->route('My.Command.DoSomething')->to('My.Command.DoSomethingHandler'); -$commandBus->utilize($router); +$router->attachToMessageBus($commandBus); ``` With this technique you can configure the routing for all your messages without the need to create all message handlers @@ -192,7 +190,7 @@ $messageProducerPlugin = new \Prooph\ServiceBus\Plugin\MessageProducerPlugin($ze $eventBus = new \Prooph\ServiceBus\EventBus(); -$eventBus->utilize($messageProducerPlugin); +$messageProducerPlugin->attachToMessageBus($eventBus); //Each event will now be routed to the async message producer $eventBus->dispatch($domainEvent); diff --git a/examples/quick-start.php b/examples/quick-start.php index a6b1d85..7d195ee 100644 --- a/examples/quick-start.php +++ b/examples/quick-start.php @@ -1,19 +1,20 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace { require_once __DIR__ . '/../vendor/autoload.php'; } namespace Prooph\ServiceBus\Example\Command { - use Prooph\Common\Messaging\Command; class EchoText extends Command @@ -23,33 +24,30 @@ class EchoText extends Command */ private $text; - public function __construct($text) + protected $messageName = 'Prooph\ServiceBus\Example\Command\EchoText'; + + public function __construct(string $text) { $this->text = $text; } - public function getText() + public function getText(): string { return $this->text; } /** * Return message payload as array - * - * @return array */ - public function payload() + public function payload(): array { return ['text' => $this->text]; } /** * This method is called when message is instantiated named constructor fromArray - * - * @param array $payload - * @return void */ - protected function setPayload(array $payload) + protected function setPayload(array $payload): void { $this->text = $payload['text']; } @@ -67,12 +65,12 @@ protected function setPayload(array $payload) //Register a callback as CommandHandler for the EchoText command $router->route('Prooph\ServiceBus\Example\Command\EchoText') - ->to(function (EchoText $aCommand) { + ->to(function (EchoText $aCommand): void { echo $aCommand->getText(); }); //Expand command bus with the router plugin - $commandBus->utilize($router); + $router->attachToMessageBus($commandBus); //We create a new Command $echoText = new EchoText('It works'); diff --git a/src/Async/AsyncMessage.php b/src/Async/AsyncMessage.php index c976309..64ff33d 100644 --- a/src/Async/AsyncMessage.php +++ b/src/Async/AsyncMessage.php @@ -1,22 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Async; /** - * Class AsyncMessage - * - * The interface is used to mark Commands that are to be send via an async MessageProducer - * - * @package Prooph\ServiceBus\Router - * @author Guy Radford + * This interface is used to mark messages that are to be send via an async MessageProducer */ interface AsyncMessage { diff --git a/src/Async/MessageProducer.php b/src/Async/MessageProducer.php index d9423f4..6cdb9a9 100644 --- a/src/Async/MessageProducer.php +++ b/src/Async/MessageProducer.php @@ -1,26 +1,23 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Async; use Prooph\Common\Messaging\Message; -use Prooph\ServiceBus\Exception\RuntimeException; use React\Promise\Deferred; /** - * Interface MessageProducer - * * The message buses treat message producers like every other message handlers. * However, this interface marks a handler as an async message producer. - * - * @package Prooph\ServiceBus\Async */ interface MessageProducer { @@ -34,11 +31,6 @@ interface MessageProducer * MUST either be resolved/rejected OR the message producer * MUST throw a Prooph\ServiceBus\Exception\RuntimeException if it cannot * handle the $deferred - * - * @param Message $message - * @param null|Deferred $deferred - * @throws RuntimeException If a $deferred is passed but producer can not handle it - * @return void */ - public function __invoke(Message $message, Deferred $deferred = null); + public function __invoke(Message $message, Deferred $deferred = null): void; } diff --git a/src/CommandBus.php b/src/CommandBus.php index 1f8ff9d..80fd0c6 100644 --- a/src/CommandBus.php +++ b/src/CommandBus.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus; use Prooph\Common\Event\ActionEvent; @@ -16,13 +18,8 @@ use Prooph\ServiceBus\Exception\RuntimeException; /** - * Class CommandBus - * * A command bus is capable of dispatching a message to a command handler. * Only one handler per message is allowed! - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch */ class CommandBus extends MessageBus { @@ -36,91 +33,79 @@ class CommandBus extends MessageBus */ private $isDispatching = false; - /** - * Inject an ActionEventDispatcher instance - * - * @param ActionEventEmitter $actionEventDispatcher - * @return void - */ - public function setActionEventEmitter(ActionEventEmitter $actionEventDispatcher) + public function __construct(ActionEventEmitter $actionEventEmitter = null) { - $actionEventDispatcher->attachListener(self::EVENT_INVOKE_HANDLER, function (ActionEvent $actionEvent) { - $commandHandler = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); + parent::__construct($actionEventEmitter); - if (is_callable($commandHandler)) { - $command = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); - $commandHandler($command); - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); - } - }); + $this->events->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $commandHandler = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); - $this->events = $actionEventDispatcher; + if (is_callable($commandHandler)) { + $command = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); + $commandHandler($command); + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); + } + }, + self::PRIORITY_INVOKE_HANDLER + ); + + $this->events->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + throw new RuntimeException(sprintf( + 'CommandBus was not able to identify a CommandHandler for command %s', + $this->getMessageName($actionEvent->getParam(self::EVENT_PARAM_MESSAGE)) + )); + } + }, + self::PRIORITY_LOCATE_HANDLER + ); } /** * @param mixed $command + * * @throws CommandDispatchException - * @return void */ - public function dispatch($command) + public function dispatch($command): void { $this->commandQueue[] = $command; if (! $this->isDispatching) { $this->isDispatching = true; + $actionEventEmitter = $this->events; + try { while ($command = array_shift($this->commandQueue)) { - $this->processCommand($command); + $actionEvent = $actionEventEmitter->getNewActionEvent( + self::EVENT_DISPATCH, + $this, + [ + self::EVENT_PARAM_MESSAGE => $command, + ] + ); + + try { + $actionEventEmitter->dispatch($actionEvent); + + if (! $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLED)) { + throw new RuntimeException(sprintf('Command %s was not handled', $this->getMessageName($command))); + } + } catch (\Throwable $exception) { + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $exception); + } finally { + $this->triggerFinalize($actionEvent); + } } $this->isDispatching = false; - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->isDispatching = false; throw CommandDispatchException::wrap($e, $this->commandQueue); } } } - - protected function processCommand($command) - { - $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); - - $actionEvent->setTarget($this); - - try { - $this->initialize($command, $actionEvent); - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { - $actionEvent->setName(self::EVENT_ROUTE); - - $this->trigger($actionEvent); - } - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { - throw new RuntimeException(sprintf( - "CommandBus was not able to identify a CommandHandler for command %s", - $this->getMessageName($command) - )); - } - - $handler = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); - - if (is_string($handler) && ! is_callable($handler)) { - $actionEvent->setName(self::EVENT_LOCATE_HANDLER); - - $this->trigger($actionEvent); - } - - $actionEvent->setName(self::EVENT_INVOKE_HANDLER); - $this->trigger($actionEvent); - - if (! $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLED)) { - throw new RuntimeException(sprintf('Command %s was not handled', $this->getMessageName($command))); - } - - $this->triggerFinalize($actionEvent); - } catch (\Exception $ex) { - $this->handleException($actionEvent, $ex); - } - } } diff --git a/src/Container/AbstractBusFactory.php b/src/Container/AbstractBusFactory.php index 51543b1..2198312 100644 --- a/src/Container/AbstractBusFactory.php +++ b/src/Container/AbstractBusFactory.php @@ -1,19 +1,20 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container; use Interop\Config\ConfigurationTrait; -use Interop\Config\RequiresConfigId; use Interop\Config\ProvidesDefaultOptions; -use Interop\Container\ContainerInterface; +use Interop\Config\RequiresConfigId; use Prooph\Common\Messaging\MessageFactory; use Prooph\ServiceBus\Exception\InvalidArgumentException; use Prooph\ServiceBus\Exception\RuntimeException; @@ -21,30 +22,21 @@ use Prooph\ServiceBus\Plugin\MessageFactoryPlugin; use Prooph\ServiceBus\Plugin\Router\AsyncSwitchMessageRouter; use Prooph\ServiceBus\Plugin\ServiceLocatorPlugin; +use Psr\Container\ContainerInterface; -/** - * Class AbstractBusFactory - * - * @package Prooph\ServiceBus\Container - * @author Alexander Miertsch - */ abstract class AbstractBusFactory implements RequiresConfigId, ProvidesDefaultOptions { use ConfigurationTrait; /** * Returns the FQCN of a bus extending Prooph\ServiceBus\MessageBus - * - * @return string */ - abstract protected function getBusClass(); + abstract protected function getBusClass(): string; /** * Returns the default router class to use if no one was specified in the config - * - * @return string */ - abstract protected function getDefaultRouterClass(); + abstract protected function getDefaultRouterClass(): string; /** * @var string @@ -64,42 +56,30 @@ abstract protected function getDefaultRouterClass(); * ]; * * - * @param string $name - * @param array $arguments - * @return mixed * @throws InvalidArgumentException */ - public static function __callStatic($name, array $arguments) + public static function __callStatic(string $name, array $arguments): MessageBus { - if (!isset($arguments[0]) || !$arguments[0] instanceof ContainerInterface) { + if (! isset($arguments[0]) || ! $arguments[0] instanceof ContainerInterface) { throw new InvalidArgumentException( sprintf('The first argument must be of type %s', ContainerInterface::class) ); } + return (new static($name))->__invoke($arguments[0]); } - /** - * @param string $configId - */ - public function __construct($configId) + public function __construct(string $configId) { - // ensure BC - $this->configId = method_exists($this, 'containerId') ? $this->containerId() : $configId; + $this->configId = $configId; } - /** - * @inheritdoc - */ - public function dimensions() + public function dimensions(): iterable { return ['prooph', 'service_bus']; } - /** - * @inheritdoc - */ - public function defaultOptions() + public function defaultOptions(): iterable { return [ 'enable_handler_location' => true, @@ -107,14 +87,7 @@ public function defaultOptions() ]; } - /** - * Create service. - * - * @param ContainerInterface $container - * @throws RuntimeException - * @return MessageBus - */ - public function __invoke(ContainerInterface $container) + public function __invoke(ContainerInterface $container): MessageBus { $config = []; @@ -137,46 +110,35 @@ public function __invoke(ContainerInterface $container) } if ((bool) $busConfig['enable_handler_location']) { - $bus->utilize(new ServiceLocatorPlugin($container)); + (new ServiceLocatorPlugin($container))->attachToMessageBus($bus); } if ($container->has($busConfig['message_factory'])) { - $bus->utilize(new MessageFactoryPlugin($container->get($busConfig['message_factory']))); + (new MessageFactoryPlugin($container->get($busConfig['message_factory'])))->attachToMessageBus($bus); } return $bus; } - /** - * @param MessageBus $bus - * @param array $utils - * @param ContainerInterface $container - * @throws RuntimeException - */ - private function attachPlugins(MessageBus $bus, array $utils, ContainerInterface $container) + private function attachPlugins(MessageBus $bus, array $plugins, ContainerInterface $container): void { - foreach ($utils as $index => $util) { - if (! is_string($util) || ! $container->has($util)) { + foreach ($plugins as $index => $plugin) { + if (! is_string($plugin) || ! $container->has($plugin)) { throw new RuntimeException(sprintf( 'Wrong message bus utility configured at %s. Either it is not a string or unknown by the container.', implode('.', $this->dimensions()) . '.' . $this->configId . '.' . $index )); } - $bus->utilize($container->get($util)); + $container->get($plugin)->attachToMessageBus($bus); } } - /** - * @param MessageBus $bus - * @param array $routerConfig - * @param ContainerInterface $container - */ - private function attachRouter(MessageBus $bus, array $routerConfig, ContainerInterface $container) + private function attachRouter(MessageBus $bus, array $routerConfig, ContainerInterface $container): void { - $routerClass = isset($routerConfig['type']) ? (string)$routerConfig['type'] : $this->getDefaultRouterClass(); + $routerClass = $routerConfig['type'] ?? $this->getDefaultRouterClass(); - $routes = isset($routerConfig['routes']) ? $routerConfig['routes'] : []; + $routes = $routerConfig['routes'] ?? []; $router = new $routerClass($routes); @@ -186,6 +148,6 @@ private function attachRouter(MessageBus $bus, array $routerConfig, ContainerInt $router = new AsyncSwitchMessageRouter($router, $asyncMessageProducer); } - $bus->utilize($router); + $router->attachToMessageBus($bus); } } diff --git a/src/Container/CommandBusFactory.php b/src/Container/CommandBusFactory.php index c69bd8f..82b0d9c 100644 --- a/src/Container/CommandBusFactory.php +++ b/src/Container/CommandBusFactory.php @@ -1,46 +1,33 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\Plugin\Router\CommandRouter; -/** - * Class CommandBusFactory - * - * @package Prooph\ServiceBus\Container - * @author Alexander Miertsch - */ class CommandBusFactory extends AbstractBusFactory { - /** - * @inheritdoc - */ - public function __construct($configId = 'command_bus') + public function __construct(string $configId = 'command_bus') { parent::__construct($configId); } - /** - * @inheritdoc - */ - protected function getBusClass() + protected function getBusClass(): string { return CommandBus::class; } - /** - * @inheritdoc - */ - protected function getDefaultRouterClass() + protected function getDefaultRouterClass(): string { return CommandRouter::class; } diff --git a/src/Container/EventBusFactory.php b/src/Container/EventBusFactory.php index 23167e5..1d7d236 100644 --- a/src/Container/EventBusFactory.php +++ b/src/Container/EventBusFactory.php @@ -1,46 +1,33 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Plugin\Router\EventRouter; -/** - * Class EventBusFactory - * - * @package Prooph\ServiceBus\Container - * @author Alexander Miertsch - */ class EventBusFactory extends AbstractBusFactory { - /** - * @inheritdoc - */ - public function __construct($configId = 'event_bus') + public function __construct(string $configId = 'event_bus') { parent::__construct($configId); } - /** - * @inheritdoc - */ - protected function getBusClass() + protected function getBusClass(): string { return EventBus::class; } - /** - * @inheritdoc - */ - protected function getDefaultRouterClass() + protected function getDefaultRouterClass(): string { return EventRouter::class; } diff --git a/src/Container/Plugin/Guard/FinalizeGuardFactory.php b/src/Container/Plugin/Guard/FinalizeGuardFactory.php index c9f695f..1877c66 100644 --- a/src/Container/Plugin/Guard/FinalizeGuardFactory.php +++ b/src/Container/Plugin/Guard/FinalizeGuardFactory.php @@ -1,36 +1,30 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container\Plugin\Guard; -use Interop\Container\ContainerInterface; use Prooph\ServiceBus\Exception\InvalidArgumentException; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\FinalizeGuard; +use Psr\Container\ContainerInterface; -/** - * Class FinalizeGuardFactory - * @package Prooph\ServiceBus\Container\Plugin\Guard - */ -final class FinalizeGuardFactory +class FinalizeGuardFactory { /** * @var bool */ private $exposeEventMessageName; - /** - * FinalizeGuardFactory constructor. - * @param bool $exposeEventMessageName - */ - public function __construct($exposeEventMessageName = false) + public function __construct(bool $exposeEventMessageName = false) { $this->exposeEventMessageName = $exposeEventMessageName; } @@ -50,14 +44,11 @@ public function __construct($exposeEventMessageName = false) * ]; * * - * @param string $name - * @param array $arguments - * @return \Prooph\ServiceBus\Plugin\Guard\FinalizeGuard * @throws InvalidArgumentException */ - public static function __callStatic($name, array $arguments) + public static function __callStatic(string $name, array $arguments): FinalizeGuard { - if (!isset($arguments[0]) || !$arguments[0] instanceof ContainerInterface) { + if (! isset($arguments[0]) || ! $arguments[0] instanceof ContainerInterface) { throw new InvalidArgumentException( sprintf('The first argument must be of type %s', ContainerInterface::class) ); @@ -66,11 +57,7 @@ public static function __callStatic($name, array $arguments) return (new static(true))->__invoke($arguments[0]); } - /** - * @param ContainerInterface $container - * @return FinalizeGuard - */ - public function __invoke(ContainerInterface $container) + public function __invoke(ContainerInterface $container): FinalizeGuard { $authorizationService = $container->get(AuthorizationService::class); diff --git a/src/Container/Plugin/Guard/RouteGuardFactory.php b/src/Container/Plugin/Guard/RouteGuardFactory.php index 3047832..8b00607 100644 --- a/src/Container/Plugin/Guard/RouteGuardFactory.php +++ b/src/Container/Plugin/Guard/RouteGuardFactory.php @@ -1,36 +1,30 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container\Plugin\Guard; -use Interop\Container\ContainerInterface; use Prooph\ServiceBus\Exception\InvalidArgumentException; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\RouteGuard; +use Psr\Container\ContainerInterface; -/** - * Class RouteGuardFactory - * @package Prooph\ServiceBus\Container\Plugin\Guard - */ -final class RouteGuardFactory +class RouteGuardFactory { /** * @var bool */ private $exposeEventMessageName; - /** - * RouteGuardFactory constructor. - * @param bool $exposeEventMessageName - */ - public function __construct($exposeEventMessageName = false) + public function __construct(bool $exposeEventMessageName = false) { $this->exposeEventMessageName = $exposeEventMessageName; } @@ -50,14 +44,11 @@ public function __construct($exposeEventMessageName = false) * ]; * * - * @param string $name - * @param array $arguments - * @return \Prooph\ServiceBus\Plugin\Guard\RouteGuard * @throws InvalidArgumentException */ - public static function __callStatic($name, array $arguments) + public static function __callStatic($name, array $arguments): RouteGuard { - if (!isset($arguments[0]) || !$arguments[0] instanceof ContainerInterface) { + if (! isset($arguments[0]) || ! $arguments[0] instanceof ContainerInterface) { throw new InvalidArgumentException( sprintf('The first argument must be of type %s', ContainerInterface::class) ); @@ -66,11 +57,7 @@ public static function __callStatic($name, array $arguments) return (new static(true))->__invoke($arguments[0]); } - /** - * @param ContainerInterface $container - * @return RouteGuard - */ - public function __invoke(ContainerInterface $container) + public function __invoke(ContainerInterface $container): RouteGuard { $authorizationService = $container->get(AuthorizationService::class); diff --git a/src/Container/QueryBusFactory.php b/src/Container/QueryBusFactory.php index e351b50..72e3752 100644 --- a/src/Container/QueryBusFactory.php +++ b/src/Container/QueryBusFactory.php @@ -1,45 +1,33 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Container; use Prooph\ServiceBus\Plugin\Router\QueryRouter; use Prooph\ServiceBus\QueryBus; -/** - * Class QueryBusFactory - * - * @package Prooph\ServiceBus\Container - */ class QueryBusFactory extends AbstractBusFactory { - /** - * @inheritdoc - */ - public function __construct($configId = 'query_bus') + public function __construct(string $configId = 'query_bus') { parent::__construct($configId); } - /** - * @inheritdoc - */ - protected function getBusClass() + protected function getBusClass(): string { return QueryBus::class; } - /** - * @inheritdoc - */ - protected function getDefaultRouterClass() + protected function getDefaultRouterClass(): string { return QueryRouter::class; } diff --git a/src/EventBus.php b/src/EventBus.php index e45819a..f387c1c 100644 --- a/src/EventBus.php +++ b/src/EventBus.php @@ -1,82 +1,71 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus; use Prooph\Common\Event\ActionEvent; use Prooph\Common\Event\ActionEventEmitter; /** - * Class EventBus - * * An event bus is capable of dispatching a message to multiple listeners. - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch */ class EventBus extends MessageBus { - const EVENT_PARAM_EVENT_LISTENERS = 'event-listeners'; + public const EVENT_PARAM_EVENT_LISTENERS = 'event-listeners'; - /** - * Inject an ActionEventDispatcher instance - * - * @param ActionEventEmitter $actionEventDispatcher - * @return void - */ - public function setActionEventEmitter(ActionEventEmitter $actionEventDispatcher) + public function __construct(ActionEventEmitter $actionEventEmitter = null) { - $actionEventDispatcher->attachListener(self::EVENT_INVOKE_HANDLER, function (ActionEvent $actionEvent) { - $eventListener = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); - if (is_callable($eventListener)) { + parent::__construct($actionEventEmitter); + + $this->events->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { $event = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); - $eventListener($event); - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); - } - }); + $handled = false; + + foreach (array_filter($actionEvent->getParam(self::EVENT_PARAM_EVENT_LISTENERS, []), 'is_callable') as $eventListener) { + $eventListener($event); + $handled = true; + } - $this->events = $actionEventDispatcher; + if ($handled) { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); + } + }, + self::PRIORITY_INVOKE_HANDLER + ); } /** * @param mixed $event - * @return void */ - public function dispatch($event) + public function dispatch($event): void { - $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); + $actionEventEmitter = $this->events; - $actionEvent->setTarget($this); + $actionEvent = $actionEventEmitter->getNewActionEvent( + self::EVENT_DISPATCH, + $this, + [ + self::EVENT_PARAM_MESSAGE => $event, + ] + ); try { - $this->initialize($event, $actionEvent); - - $actionEvent->setName(self::EVENT_ROUTE); - - $this->trigger($actionEvent); - - foreach ($actionEvent->getParam(self::EVENT_PARAM_EVENT_LISTENERS, []) as $eventListener) { - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLER, $eventListener); - - if (is_string($eventListener) && ! is_callable($eventListener)) { - $actionEvent->setName(self::EVENT_LOCATE_HANDLER); - $this->trigger($actionEvent); - } - - $actionEvent->setName(self::EVENT_INVOKE_HANDLER); - $this->trigger($actionEvent); - } - + $actionEventEmitter->dispatch($actionEvent); + } catch (\Throwable $exception) { + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $exception); + } finally { $this->triggerFinalize($actionEvent); - } catch (\Exception $ex) { - $this->handleException($actionEvent, $ex); } } } diff --git a/src/Exception/CommandDispatchException.php b/src/Exception/CommandDispatchException.php index 80c602c..e4e76c9 100644 --- a/src/Exception/CommandDispatchException.php +++ b/src/Exception/CommandDispatchException.php @@ -1,50 +1,42 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Exception; -/** - * Class CommandDispatchException - * - * @package Prooph\ServiceBus\Exception - */ class CommandDispatchException extends MessageDispatchException { - private $pendingCommands = []; - /** - * @param \Exception $dispatchException - * @param array $pendingCommands - * @return CommandDispatchException + * @var array */ - public static function wrap(\Exception $dispatchException, array $pendingCommands) + private $pendingCommands = []; + + public static function wrap(\Throwable $dispatchException, array $pendingCommands): CommandDispatchException { if ($dispatchException instanceof MessageDispatchException) { - $ex = parent::failed($dispatchException->getFailedDispatchEvent(), $dispatchException->getPrevious()); + $ex = parent::failed($dispatchException->getPrevious()); $ex->pendingCommands = $pendingCommands; return $ex; } - $ex = new static("Command dispatch failed. See previous exception for details.", 422, $dispatchException); + $ex = new static('Command dispatch failed. See previous exception for details.', 422, $dispatchException); $ex->pendingCommands = $pendingCommands; return $ex; } - /** - * @return array - */ - public function getPendingCommands() + public function getPendingCommands(): array { return $this->pendingCommands; } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 245eaf7..04300ed 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -1,20 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Exception; -/** - * Class InvalidArgumentException - * - * @package Prooph\ServiceBus\Exception - */ class InvalidArgumentException extends \InvalidArgumentException implements ServiceBusException { } diff --git a/src/Exception/MessageDispatchException.php b/src/Exception/MessageDispatchException.php index f51a8e7..dafa90d 100644 --- a/src/Exception/MessageDispatchException.php +++ b/src/Exception/MessageDispatchException.php @@ -1,23 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Exception; use Prooph\Common\Event\ActionEvent; -/** - * Class MessageDispatchException - * - * @package Prooph\ServiceBus\Exception - * @author Alexander Miertsch - */ class MessageDispatchException extends RuntimeException { /** @@ -25,38 +21,8 @@ class MessageDispatchException extends RuntimeException */ protected $actionEvent; - /** - * @param ActionEvent $actionEvent - * @param \Exception $previousException - * @return MessageDispatchException - */ - public static function failed(ActionEvent $actionEvent, \Exception $previousException = null) - { - $ex = new static( - sprintf( - "Message dispatch failed during %s phase.%s", - $actionEvent->getName(), - (null === $previousException) ? '' : ' Error: ' . $previousException->getMessage() - ), - 422, - $previousException - ); - - $ex->setFailedDispatch($actionEvent); - - return $ex; - } - - /** - * @return ActionEvent - */ - public function getFailedDispatchEvent() - { - return $this->actionEvent; - } - - protected function setFailedDispatch(ActionEvent $actionEvent) + public static function failed(\Throwable $dispatchException): MessageDispatchException { - $this->actionEvent = $actionEvent; + return new static('Message dispatch failed. See previous exception for details.', 422, $dispatchException); } } diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index c23414a..8344648 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -1,21 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Exception; -/** - * Class RuntimeException - * - * @package Prooph\ServiceBus\Exception - * @author Alexander Miertsch - */ class RuntimeException extends \RuntimeException implements ServiceBusException { } diff --git a/src/Exception/ServiceBusException.php b/src/Exception/ServiceBusException.php index fbdf1a7..dcd913a 100644 --- a/src/Exception/ServiceBusException.php +++ b/src/Exception/ServiceBusException.php @@ -1,21 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Exception; -/** - * Interface ServiceBusException - * - * @package Prooph\ServiceBus\Exception - * @author Alexander Miertsch - */ interface ServiceBusException { } diff --git a/src/MessageBus.php b/src/MessageBus.php index ffc4cb1..50c00c4 100644 --- a/src/MessageBus.php +++ b/src/MessageBus.php @@ -1,188 +1,115 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus; use Prooph\Common\Event\ActionEvent; use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; +use Prooph\Common\Event\ListenerHandler; use Prooph\Common\Event\ProophActionEventEmitter; use Prooph\Common\Messaging\HasMessageName; use Prooph\ServiceBus\Exception\MessageDispatchException; -use Prooph\ServiceBus\Exception\RuntimeException; /** - * Class MessageBus - * * Base class for a message bus implementation - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch */ abstract class MessageBus { - const EVENT_INITIALIZE = "initialize"; - const EVENT_DETECT_MESSAGE_NAME = "detect-message-name"; - const EVENT_ROUTE = "route"; - const EVENT_LOCATE_HANDLER = "locate-handler"; - const EVENT_INVOKE_HANDLER = "invoke-handler"; - const EVENT_HANDLE_ERROR = "handle-error"; - const EVENT_FINALIZE = "finalize"; - - const EVENT_PARAM_MESSAGE = 'message'; - const EVENT_PARAM_MESSAGE_NAME = 'message-name'; - const EVENT_PARAM_MESSAGE_HANDLER = 'message-handler'; - const EVENT_PARAM_EXCEPTION = 'exception'; - const EVENT_PARAM_MESSAGE_HANDLED = 'message-handled'; + public const EVENT_DISPATCH = 'dispatch'; + public const EVENT_FINALIZE = 'finalize'; + + public const EVENT_PARAM_MESSAGE = 'message'; + public const EVENT_PARAM_MESSAGE_NAME = 'message-name'; + public const EVENT_PARAM_MESSAGE_HANDLER = 'message-handler'; + public const EVENT_PARAM_EXCEPTION = 'exception'; + public const EVENT_PARAM_MESSAGE_HANDLED = 'message-handled'; + + public const PRIORITY_INITIALIZE = 400000; + public const PRIORITY_DETECT_MESSAGE_NAME = 300000; + public const PRIORITY_ROUTE = 200000; + public const PRIORITY_LOCATE_HANDLER = 100000; + public const PRIORITY_PROMISE_REJECT = 1000; + public const PRIORITY_INVOKE_HANDLER = 0; /** * @var ActionEventEmitter */ protected $events; - /** - * @param mixed $message - * @return mixed|void depends on the bus type - */ - abstract public function dispatch($message); - - /** - * @param ActionEventListenerAggregate $plugin - */ - public function utilize(ActionEventListenerAggregate $plugin) - { - $plugin->attach($this->getActionEventEmitter()); - } - - /** - * @param ActionEventListenerAggregate $plugin - */ - public function deactivate(ActionEventListenerAggregate $plugin) - { - $plugin->detach($this->getActionEventEmitter()); - } - - /** - * @param mixed $message - * @param ActionEvent $actionEvent - */ - protected function initialize($message, ActionEvent $actionEvent) + public function __construct(ActionEventEmitter $actionEventEmitter = null) { - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE, $message); - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, false); - - if ($message instanceof HasMessageName) { - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_NAME, $message->messageName()); + if (null === $actionEventEmitter) { + $actionEventEmitter = new ProophActionEventEmitter([ + self::EVENT_DISPATCH, + self::EVENT_FINALIZE, + ]); } - $actionEvent->setName(self::EVENT_INITIALIZE); - - $this->trigger($actionEvent); - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_NAME) === null) { - $actionEvent->setName(self::EVENT_DETECT_MESSAGE_NAME); - - $this->trigger($actionEvent); - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_NAME) === null) { - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_NAME, $this->getMessageName($message)); + $actionEventEmitter->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, false); + $message = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); + + if ($message instanceof HasMessageName) { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_NAME, $message->messageName()); + } + }, + self::PRIORITY_INITIALIZE + ); + + $actionEventEmitter->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_NAME) === null) { + $actionEvent->setParam( + self::EVENT_PARAM_MESSAGE_NAME, + $this->getMessageName($actionEvent->getParam(self::EVENT_PARAM_MESSAGE)) + ); + } + }, + self::PRIORITY_DETECT_MESSAGE_NAME + ); + + $actionEventEmitter->attachListener( + self::EVENT_FINALIZE, + function (ActionEvent $actionEvent): void { + if ($exception = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { + throw MessageDispatchException::failed($exception); + } } - } - } - - /** - * @param ActionEvent $actionEvent - * @param \Exception $ex - * @throws Exception\MessageDispatchException - */ - protected function handleException(ActionEvent $actionEvent, \Exception $ex) - { - $failedPhase = $actionEvent->getName(); + ); - $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $ex); - $this->triggerError($actionEvent); - $this->triggerFinalize($actionEvent); - - //Check if a listener has removed the exception to indicate that it was able to handle it - if ($ex = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { - $actionEvent->setName($failedPhase); - throw MessageDispatchException::failed($actionEvent, $ex); - } + $this->events = $actionEventEmitter; } /** - * @param ActionEvent $actionEvent - * @throws Exception\RuntimeException - */ - protected function trigger(ActionEvent $actionEvent) - { - $this->getActionEventEmitter()->dispatch($actionEvent); - - if ($actionEvent->propagationIsStopped()) { - throw new RuntimeException("Dispatch has stopped unexpectedly."); - } - } - - /** - * @param ActionEvent $actionEvent - */ - protected function triggerError(ActionEvent $actionEvent) - { - $actionEvent->setName(self::EVENT_HANDLE_ERROR); - - $this->getActionEventEmitter()->dispatch($actionEvent); - } - - /** - * @param ActionEvent $actionEvent - */ - protected function triggerFinalize(ActionEvent $actionEvent) - { - $actionEvent->setName(self::EVENT_FINALIZE); - - $this->getActionEventEmitter()->dispatch($actionEvent); - } - - - /** - * Inject an ActionEventDispatcher instance + * @param mixed $message * - * @param ActionEventEmitter $actionEventDispatcher - * @return void + * @return \React\Promise\Promise|void depends on the bus type */ - public function setActionEventEmitter(ActionEventEmitter $actionEventDispatcher) - { - $this->events = $actionEventDispatcher; - } + abstract public function dispatch($message); - /** - * Retrieve the action event dispatcher - * - * Lazy-loads a dispatcher if none is registered. - * - * @return ActionEventEmitter - */ - public function getActionEventEmitter() + protected function triggerFinalize(ActionEvent $actionEvent): void { - if (null === $this->events) { - $this->setActionEventEmitter(new ProophActionEventEmitter()); - } + $actionEvent->setName(self::EVENT_FINALIZE); - return $this->events; + $this->events->dispatch($actionEvent); } /** * @param mixed $message - * @return string */ - protected function getMessageName($message) + protected function getMessageName($message): string { if (is_object($message)) { return get_class($message); @@ -194,4 +121,14 @@ protected function getMessageName($message) return gettype($message); } + + public function attach(string $eventName, callable $listener, int $priority = 0): ListenerHandler + { + return $this->events->attachListener($eventName, $listener, $priority); + } + + public function detach(ListenerHandler $handler): void + { + $this->events->detachListener($handler); + } } diff --git a/src/Plugin/AbstractPlugin.php b/src/Plugin/AbstractPlugin.php new file mode 100644 index 0000000..7a96d8a --- /dev/null +++ b/src/Plugin/AbstractPlugin.php @@ -0,0 +1,32 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Prooph\ServiceBus\Plugin; + +use Prooph\ServiceBus\MessageBus; + +abstract class AbstractPlugin implements Plugin +{ + /** + * @var array + */ + protected $listenerHandlers = []; + + public function detachFromMessageBus(MessageBus $messageBus): void + { + foreach ($this->listenerHandlers as $listenerHandler) { + $messageBus->detach($listenerHandler); + } + + $this->listenerHandlers = []; + } +} diff --git a/src/Plugin/Guard/AuthorizationService.php b/src/Plugin/Guard/AuthorizationService.php index eb47293..4b559e2 100644 --- a/src/Plugin/Guard/AuthorizationService.php +++ b/src/Plugin/Guard/AuthorizationService.php @@ -1,19 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Guard; -/** - * Interface AuthorizationService - * @package Prooph\ServiceBus\Plugin\Guard - */ interface AuthorizationService { /** @@ -21,7 +19,8 @@ interface AuthorizationService * * @param string $messageName * @param mixed $context + * * @return bool */ - public function isGranted($messageName, $context = null); + public function isGranted(string $messageName, $context = null): bool; } diff --git a/src/Plugin/Guard/FinalizeGuard.php b/src/Plugin/Guard/FinalizeGuard.php index 8e246c0..daf75e3 100644 --- a/src/Plugin/Guard/FinalizeGuard.php +++ b/src/Plugin/Guard/FinalizeGuard.php @@ -1,31 +1,25 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Guard; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; use Prooph\ServiceBus\QueryBus; use React\Promise\Promise; -/** - * Class FinalizeGuard - * @package Prooph\ServiceBus\Plugin\Guard - */ -final class FinalizeGuard implements ActionEventListenerAggregate +final class FinalizeGuard extends AbstractPlugin { - use DetachAggregateHandlers; - /** * @var AuthorizationService */ @@ -36,28 +30,35 @@ final class FinalizeGuard implements ActionEventListenerAggregate */ private $exposeEventMessageName; - /** - * @param AuthorizationService $authorizationService - * @param bool $exposeEventMessageName - */ - public function __construct(AuthorizationService $authorizationService, $exposeEventMessageName = false) + public function __construct(AuthorizationService $authorizationService, bool $exposeEventMessageName = false) { $this->authorizationService = $authorizationService; $this->exposeEventMessageName = $exposeEventMessageName; } - /** - * @param ActionEvent $actionEvent - * @throws UnauthorizedException - */ - public function onFinalize(ActionEvent $actionEvent) + public function attachToMessageBus(MessageBus $messageBus): void { - $promise = $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE); - $messageName = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent): void { + $promise = $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE); + $messageName = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if ($promise instanceof Promise) { + $newPromise = $promise->then(function ($result) use ($actionEvent, $messageName): void { + if (! $this->authorizationService->isGranted($messageName, $result)) { + $actionEvent->stopPropagation(true); + + if (! $this->exposeEventMessageName) { + $messageName = ''; + } - if ($promise instanceof Promise) { - $newPromise = $promise->then(function ($result) use ($actionEvent, $messageName) { - if (!$this->authorizationService->isGranted($messageName, $result)) { + throw new UnauthorizedException($messageName); + } + }); + + $actionEvent->setParam(QueryBus::EVENT_PARAM_PROMISE, $newPromise); + } elseif (! $this->authorizationService->isGranted($messageName)) { $actionEvent->stopPropagation(true); if (! $this->exposeEventMessageName) { @@ -66,27 +67,8 @@ public function onFinalize(ActionEvent $actionEvent) throw new UnauthorizedException($messageName); } - }); - - $actionEvent->setParam(QueryBus::EVENT_PARAM_PROMISE, $newPromise); - } elseif (!$this->authorizationService->isGranted($messageName)) { - $actionEvent->stopPropagation(true); - - if (! $this->exposeEventMessageName) { - $messageName = ''; - } - - throw new UnauthorizedException($messageName); - } - } - - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) - { - $this->trackHandler($events->attachListener(MessageBus::EVENT_FINALIZE, [$this, "onFinalize"], -1000)); + }, + -1000 + ); } } diff --git a/src/Plugin/Guard/RouteGuard.php b/src/Plugin/Guard/RouteGuard.php index 46a62a2..a7aa0fb 100644 --- a/src/Plugin/Guard/RouteGuard.php +++ b/src/Plugin/Guard/RouteGuard.php @@ -1,29 +1,23 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Guard; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; -/** - * Class RouteGuard - * @package Prooph\ServiceBus\Plugin\Guard - */ -final class RouteGuard implements ActionEventListenerAggregate +final class RouteGuard extends AbstractPlugin { - use DetachAggregateHandlers; - /** * @var AuthorizationService */ @@ -34,46 +28,35 @@ final class RouteGuard implements ActionEventListenerAggregate */ private $exposeEventMessageName; - /** - * @param AuthorizationService $authorizationService - * @param bool $exposeEventMessageName - */ - public function __construct(AuthorizationService $authorizationService, $exposeEventMessageName = false) + public function __construct(AuthorizationService $authorizationService, bool $exposeEventMessageName = false) { $this->authorizationService = $authorizationService; $this->exposeEventMessageName = $exposeEventMessageName; } - /** - * @param ActionEvent $actionEvent - */ - public function onRoute(ActionEvent $actionEvent) - { - $messageName = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); - - if ($this->authorizationService->isGranted( - $messageName, - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE) - )) { - return; - } - - $actionEvent->stopPropagation(true); - - if (! $this->exposeEventMessageName) { - $messageName = ''; - } - - throw new UnauthorizedException($messageName); - } - - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) + public function attachToMessageBus(MessageBus $messageBus): void { - $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, "onRoute"], 1000)); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $messageName = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if ($this->authorizationService->isGranted( + $messageName, + $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE) + )) { + return; + } + + $actionEvent->stopPropagation(true); + + if (! $this->exposeEventMessageName) { + $messageName = ''; + } + + throw new UnauthorizedException($messageName); + }, + MessageBus::PRIORITY_ROUTE + ); } } diff --git a/src/Plugin/Guard/UnauthorizedException.php b/src/Plugin/Guard/UnauthorizedException.php index ea96aaf..a7d3b4a 100644 --- a/src/Plugin/Guard/UnauthorizedException.php +++ b/src/Plugin/Guard/UnauthorizedException.php @@ -1,31 +1,25 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Guard; -/** - * Class UnauthorizedException - * @package Prooph\ServiceBus\Plugin\Guard - */ -final class UnauthorizedException extends \RuntimeException +class UnauthorizedException extends \RuntimeException { /** * @var string */ protected $message = 'You are not authorized to access this resource'; - /** - * UnauthorizedException constructor. - * @param string $messageName - */ - public function __construct($messageName = '') + public function __construct(string $messageName = '') { if (! empty($messageName)) { $this->message = 'You are not authorized to access the resource "' . $messageName . '"'; diff --git a/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php b/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php deleted file mode 100644 index 22dc867..0000000 --- a/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php +++ /dev/null @@ -1,72 +0,0 @@ - - * (c) 2015-2016 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prooph\ServiceBus\Plugin\InvokeStrategy; - -use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\ServiceBus\MessageBus; - -/** - * Class AbstractInvokeStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -abstract class AbstractInvokeStrategy implements ActionEventListenerAggregate -{ - use DetachAggregateHandlers; - - /** - * @var int - */ - protected $priority = 0; - - /** - * @param mixed $handler - * @param mixed $message - * @return bool - */ - abstract protected function canInvoke($handler, $message); - - /** - * @param mixed $handler - * @param mixed $message - */ - abstract protected function invoke($handler, $message); - - /** - * Attach one or more listeners - * - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) - { - $this->trackHandler($events->attachListener(MessageBus::EVENT_INVOKE_HANDLER, $this, $this->priority)); - } - - /** - * @param ActionEvent $e - */ - public function __invoke(ActionEvent $e) - { - $message = $e->getParam(MessageBus::EVENT_PARAM_MESSAGE); - $handler = $e->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); - - if ($this->canInvoke($handler, $message)) { - $this->invoke($handler, $message); - $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); - } - } -} diff --git a/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php b/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php index 8aa9244..673359d 100644 --- a/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php +++ b/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php @@ -1,74 +1,41 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\InvokeStrategy; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\Common\Messaging\HasMessageName; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; use Prooph\ServiceBus\QueryBus; -/** - * Class FinderInvokeStrategy - * - * This is a special invoke strategy for finders handling a query message and providing a response by resolving the - * deferred of the query dispatch. - * - * The invoke strategy can handle finders which have a method named like the short name of the query. - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -final class FinderInvokeStrategy implements ActionEventListenerAggregate +class FinderInvokeStrategy extends AbstractPlugin { - use DetachAggregateHandlers; - - /** - * @param ActionEventEmitter $dispatcher - */ - public function attach(ActionEventEmitter $dispatcher) - { - $this->trackHandler($dispatcher->attachListener(QueryBus::EVENT_INVOKE_FINDER, $this)); - } - - /** - * @param ActionEvent $actionEvent - */ - public function __invoke(ActionEvent $actionEvent) - { - $finder = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER); - - $query = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE); - - $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); - - if (is_object($finder)) { - $queryName = $this->determineQueryName($query); - - if (method_exists($finder, $queryName)) { - $finder->{$queryName}($query, $deferred); - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); - } - } - } - - /** - * @param mixed $query - * @return string - */ - private function determineQueryName($query) + public function attachToMessageBus(MessageBus $messageBus): void { - $queryName = ($query instanceof HasMessageName)? $query->messageName() : (is_object($query)? get_class($query) : gettype($query)); - return implode('', array_slice(explode('\\', $queryName), -1)); + $this->listenerHandlers[] = $messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $finder = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER); + + $query = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE); + + $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); + + if (is_object($finder)) { + $finder->find($query, $deferred); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); + } + }, + QueryBus::PRIORITY_INVOKE_HANDLER + ); } } diff --git a/src/Plugin/InvokeStrategy/HandleCommandStrategy.php b/src/Plugin/InvokeStrategy/HandleCommandStrategy.php index c86654d..13b5bf5 100644 --- a/src/Plugin/InvokeStrategy/HandleCommandStrategy.php +++ b/src/Plugin/InvokeStrategy/HandleCommandStrategy.php @@ -1,59 +1,35 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\InvokeStrategy; -use Prooph\Common\Messaging\HasMessageName; +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; -/** - * Class HandleCommandStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class HandleCommandStrategy extends AbstractInvokeStrategy +class HandleCommandStrategy extends AbstractPlugin { - /** - * @param mixed $handler - * @param mixed $message - * @return bool - */ - public function canInvoke($handler, $message) + public function attachToMessageBus(MessageBus $messageBus): void { - $handleMethod = 'handle' . $this->determineCommandName($message); - - return method_exists($handler, $handleMethod) || method_exists($handler, 'handle'); - } + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); + $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); - /** - * @param mixed $handler - * @param mixed $message - */ - public function invoke($handler, $message) - { - $handleMethod = 'handle' . $this->determineCommandName($message); - - if (method_exists($handler, $handleMethod)) { - $handler->{$handleMethod}($message); - } else { - $handler->handle($message); - } - } - - /** - * @param mixed $message - * @return string - */ - protected function determineCommandName($message) - { - $eventName = ($message instanceof HasMessageName)? $message->messageName() : (is_object($message)? get_class($message) : gettype($message)); - return implode('', array_slice(explode('\\', $eventName), -1)); + $handler->handle($message); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + MessageBus::PRIORITY_INVOKE_HANDLER + ); } } diff --git a/src/Plugin/InvokeStrategy/OnEventStrategy.php b/src/Plugin/InvokeStrategy/OnEventStrategy.php index 65be5f3..b069680 100644 --- a/src/Plugin/InvokeStrategy/OnEventStrategy.php +++ b/src/Plugin/InvokeStrategy/OnEventStrategy.php @@ -1,55 +1,39 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\InvokeStrategy; -use Prooph\Common\Messaging\HasMessageName; +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; -/** - * Class OnEventStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class OnEventStrategy extends AbstractInvokeStrategy +final class OnEventStrategy extends AbstractPlugin { - /** - * @param mixed $handler - * @param mixed $message - * @return bool - */ - public function canInvoke($handler, $message) - { - $handleMethod = 'on' . $this->determineEventName($message); - - return method_exists($handler, $handleMethod); - } - - /** - * @param mixed $handler - * @param mixed $message - */ - public function invoke($handler, $message) + public function attachToMessageBus(MessageBus $messageBus): void { - $handleMethod = 'on' . $this->determineEventName($message); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); + $handlers = $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []); - $handler->{$handleMethod}($message); - } + foreach ($handlers as $handler) { + $handler->onEvent($message); + } - /** - * @param mixed $event - * @return string - */ - protected function determineEventName($event) - { - $eventName = ($event instanceof HasMessageName)? $event->messageName() : (is_object($event)? get_class($event) : gettype($event)); - return implode('', array_slice(explode('\\', $eventName), -1)); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + MessageBus::PRIORITY_INVOKE_HANDLER + ); } } diff --git a/src/Plugin/MessageFactoryPlugin.php b/src/Plugin/MessageFactoryPlugin.php index ef0d455..a57fe66 100644 --- a/src/Plugin/MessageFactoryPlugin.php +++ b/src/Plugin/MessageFactoryPlugin.php @@ -1,80 +1,64 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\Common\Messaging\MessageFactory; use Prooph\ServiceBus\MessageBus; /** - * Class MessageFactoryPlugin - * - * This plugin listens on the MessageBus::EVENT_INITIALIZE action event. + * This plugin listens on the MessageBus::EVENT_DISPATCH action event with MessageBus::PRIORITY_INITIALIZE. * It checks if the message of the action event is given as an array and * if the array contains a key "message_name". * If both conditions are met the plugin uses the injected Prooph\Common\Messaging\MessageFactory * to translate the message array into a Prooph\Common\Messaging\Message - * - * @package Prooph\ServiceBus\Plugin - * @author Alexander Miertsch */ -final class MessageFactoryPlugin implements ActionEventListenerAggregate +class MessageFactoryPlugin extends AbstractPlugin { - use DetachAggregateHandlers; - /** * @var MessageFactory */ private $messageFactory; - /** - * @param MessageFactory $messageFactory - */ public function __construct(MessageFactory $messageFactory) { $this->messageFactory = $messageFactory; } - /** - * @param ActionEventEmitter $dispatcher - */ - public function attach(ActionEventEmitter $dispatcher) - { - $this->trackHandler($dispatcher->attachListener(MessageBus::EVENT_INITIALIZE, $this)); - } - - /** - * @param ActionEvent $actionEvent - */ - public function __invoke(ActionEvent $actionEvent) + public function attachToMessageBus(MessageBus $messageBus): void { - $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); - if (! is_array($message)) { - return; - } + if (! is_array($message)) { + return; + } - if (! array_key_exists('message_name', $message)) { - return; - } + if (! array_key_exists('message_name', $message)) { + return; + } - $messageName = $message['message_name']; - unset($message['message_name']); + $messageName = $message['message_name']; + unset($message['message_name']); - $message = $this->messageFactory->createMessageFromArray($messageName, $message); + $message = $this->messageFactory->createMessageFromArray($messageName, $message); - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE, $message); - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, $messageName); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE, $message); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, $messageName); + }, + MessageBus::PRIORITY_INITIALIZE + ); } } diff --git a/src/Plugin/MessageProducerPlugin.php b/src/Plugin/MessageProducerPlugin.php index ca42b5f..20576ab 100644 --- a/src/Plugin/MessageProducerPlugin.php +++ b/src/Plugin/MessageProducerPlugin.php @@ -1,69 +1,54 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\Async\MessageProducer; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\MessageBus; /** - * Class MessageProducerPlugin - * * If the MessageProducerPlugin is attached to a message bus it routes all messages * to the Prooph\ServiceBus\Async\MessageProducer it is initialized with. - * - * @package Prooph\ServiceBus\Plugin */ -final class MessageProducerPlugin implements ActionEventListenerAggregate +class MessageProducerPlugin extends AbstractPlugin { - use DetachAggregateHandlers; - /** * @var MessageProducer */ private $messageProducer; - /** - * @param MessageProducer $messageProducer - */ public function __construct(MessageProducer $messageProducer) { $this->messageProducer = $messageProducer; } - /** - * @param ActionEventEmitter $emitter - */ - public function attach(ActionEventEmitter $emitter) - { - $this->trackHandler($emitter->attachListener(MessageBus::EVENT_INITIALIZE, [$this, 'onDispatchInitialize'])); - } - - /** - * @param ActionEvent $event - */ - public function onDispatchInitialize(ActionEvent $event) + public function attachToMessageBus(MessageBus $messageBus): void { - $bus = $event->getTarget(); - - if ($bus instanceof EventBus) { - $listeners = $event->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []); - $listeners[] = $this->messageProducer; - $event->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $listeners); - } else { - $event->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $this->messageProducer); - } + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $event): void { + $bus = $event->getTarget(); + + if ($bus instanceof EventBus) { + $listeners = $event->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []); + $listeners[] = $this->messageProducer; + $event->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $listeners); + } else { + $event->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $this->messageProducer); + } + }, + MessageBus::PRIORITY_INITIALIZE + ); } } diff --git a/src/Plugin/Plugin.php b/src/Plugin/Plugin.php new file mode 100644 index 0000000..57bd827 --- /dev/null +++ b/src/Plugin/Plugin.php @@ -0,0 +1,22 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Prooph\ServiceBus\Plugin; + +use Prooph\ServiceBus\MessageBus; + +interface Plugin +{ + public function attachToMessageBus(MessageBus $messageBus): void; + + public function detachFromMessageBus(MessageBus $messageBus): void; +} diff --git a/src/Plugin/Router/AsyncSwitchMessageRouter.php b/src/Plugin/Router/AsyncSwitchMessageRouter.php index 6aafc3d..e880c26 100644 --- a/src/Plugin/Router/AsyncSwitchMessageRouter.php +++ b/src/Plugin/Router/AsyncSwitchMessageRouter.php @@ -1,33 +1,28 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\Async\AsyncMessage; use Prooph\ServiceBus\Async\MessageProducer; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; use Prooph\ServiceBus\QueryBus; -/** - * Class AsyncSwitchMessageRouter - * - * @package Prooph\ServiceBus\Router - * @author Guy Radford - */ -class AsyncSwitchMessageRouter implements MessageBusRouterPlugin, ActionEventListenerAggregate +class AsyncSwitchMessageRouter extends AbstractPlugin implements MessageBusRouterPlugin { use DetachAggregateHandlers; @@ -41,33 +36,24 @@ class AsyncSwitchMessageRouter implements MessageBusRouterPlugin, ActionEventLis */ protected $asyncMessageProducer; - - /** - * @param MessageBusRouterPlugin $router - * @param MessageProducer $asyncMessageProducer - */ public function __construct(MessageBusRouterPlugin $router, MessageProducer $asyncMessageProducer) { $this->router = $router; $this->asyncMessageProducer = $asyncMessageProducer; } - /** - * @param ActionEventEmitter $events - * @return void - */ - public function attach(ActionEventEmitter $events) + public function attachToMessageBus(MessageBus $messageBus): void { - $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, "onRouteMessage"])); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); } - - /** - * @param ActionEvent $actionEvent - */ - public function onRouteMessage(ActionEvent $actionEvent) + public function onRouteMessage(ActionEvent $actionEvent): void { - $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); if (empty($messageName)) { return; @@ -76,7 +62,7 @@ public function onRouteMessage(ActionEvent $actionEvent) $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); //if the message is marked with AsyncMessage, but had not yet been sent via async then sent to async producer - if ($message instanceof AsyncMessage && !(isset($message->metadata()['handled-async']) && $message->metadata()['handled-async'] === true)) { + if ($message instanceof AsyncMessage && ! (isset($message->metadata()['handled-async']) && $message->metadata()['handled-async'] === true)) { //apply meta data, this is need to we can identify that the message has already been send via the async producer $message = $message->withAddedMetadata('handled-async', true); @@ -90,13 +76,10 @@ public function onRouteMessage(ActionEvent $actionEvent) $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$this->asyncMessageProducer]); } - - - return; } // pass ActionEvent to decorated router - return $this->router->onRouteMessage($actionEvent); + $this->router->onRouteMessage($actionEvent); } } diff --git a/src/Plugin/Router/CommandRouter.php b/src/Plugin/Router/CommandRouter.php index e2b05fe..ee61a14 100644 --- a/src/Plugin/Router/CommandRouter.php +++ b/src/Plugin/Router/CommandRouter.php @@ -1,21 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; -/** - * Class CommandRouter - * - * @package Prooph\ServiceBus\Router - * @author Alexander Miertsch - */ class CommandRouter extends SingleHandlerRouter { } diff --git a/src/Plugin/Router/EventRouter.php b/src/Plugin/Router/EventRouter.php index 229215b..3dc1d93 100644 --- a/src/Plugin/Router/EventRouter.php +++ b/src/Plugin/Router/EventRouter.php @@ -1,34 +1,26 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; use Assert\Assertion; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; -/** - * Class EventRouter - * - * @package Prooph\ServiceBus\Router - * @author Alexander Miertsch - */ -class EventRouter implements MessageBusRouterPlugin, ActionEventListenerAggregate +class EventRouter extends AbstractPlugin implements MessageBusRouterPlugin { - use DetachAggregateHandlers; - /** * @var array[eventName => eventListener] */ @@ -44,43 +36,38 @@ class EventRouter implements MessageBusRouterPlugin, ActionEventListenerAggregat */ public function __construct(array $eventMap = null) { - if (null !== $eventMap) { - foreach ($eventMap as $eventName => $listeners) { - if (is_string($listeners) || is_object($listeners) || is_callable($listeners)) { - $listeners = [$listeners]; - } + if (null === $eventMap) { + return; + } - $this->route($eventName); + foreach ($eventMap as $eventName => $listeners) { + if (is_string($listeners) || is_object($listeners) || is_callable($listeners)) { + $listeners = [$listeners]; + } + + $this->route($eventName); - foreach ($listeners as $listener) { - $this->to($listener); - } + foreach ($listeners as $listener) { + $this->to($listener); } } } - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) + public function attachToMessageBus(MessageBus $messageBus): void { - $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, "onRouteMessage"])); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); } - /** - * @param string $eventName - * @return $this - * @throws Exception\RuntimeException - */ - public function route($eventName) + public function route(string $eventName): EventRouter { - Assertion::string($eventName); Assertion::notEmpty($eventName); if (null !== $this->tmpEventName && empty($this->eventMap[$this->tmpEventName])) { - throw new Exception\RuntimeException(sprintf("event %s is not mapped to a listener.", $this->tmpEventName)); + throw new Exception\RuntimeException(sprintf('event %s is not mapped to a listener.', $this->tmpEventName)); } $this->tmpEventName = $eventName; @@ -94,23 +81,27 @@ public function route($eventName) /** * @param string|object|callable $eventListener - * @return $this + * + * @return EventRouter + * * @throws Exception\RuntimeException * @throws Exception\InvalidArgumentException */ - public function to($eventListener) + public function to($eventListener): EventRouter { if (! is_string($eventListener) && ! is_object($eventListener) && ! is_callable($eventListener)) { throw new Exception\InvalidArgumentException(sprintf( - "Invalid event listener provided. Expected type is string, object or callable but type of %s given.", + 'Invalid event listener provided. Expected type is string, object or callable but type of %s given.', gettype($eventListener) )); } if (null === $this->tmpEventName) { throw new Exception\RuntimeException(sprintf( - "Cannot map listener %s to an event. Please use method route before calling method to", - (is_object($eventListener))? get_class($eventListener) : (is_string($eventListener))? $eventListener : gettype($eventListener) + 'Cannot map listener %s to an event. Please use method route before calling method to', + is_object($eventListener) + ? get_class($eventListener) + : is_string($eventListener) ? $eventListener : gettype($eventListener) )); } @@ -121,26 +112,25 @@ public function to($eventListener) /** * Alias for method to + * * @param string|object|callable $eventListener - * @return $this + * + * @return EventRouter */ - public function andTo($eventListener) + public function andTo($eventListener): EventRouter { return $this->to($eventListener); } - /** - * @param ActionEvent $actionEvent - */ - public function onRouteMessage(ActionEvent $actionEvent) + public function onRouteMessage(ActionEvent $actionEvent): void { - $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); if (empty($messageName)) { return; } - if (!isset($this->eventMap[$messageName])) { + if (! isset($this->eventMap[$messageName])) { return; } @@ -150,13 +140,4 @@ public function onRouteMessage(ActionEvent $actionEvent) $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $listeners); } - - /** - * @param ActionEvent $actionEvent - * @deprecated Will be removed with v6.0, use method onRouteMessage instead - */ - public function onRouteEvent(ActionEvent $actionEvent) - { - $this->onRouteMessage($actionEvent); - } } diff --git a/src/Plugin/Router/MessageBusRouterPlugin.php b/src/Plugin/Router/MessageBusRouterPlugin.php index 4db80cf..5c7ab5b 100644 --- a/src/Plugin/Router/MessageBusRouterPlugin.php +++ b/src/Plugin/Router/MessageBusRouterPlugin.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; use Prooph\Common\Event\ActionEvent; @@ -16,9 +18,6 @@ interface MessageBusRouterPlugin { /** * Handle route action event of a message bus dispatch - * - * @param ActionEvent $actionEvent - * @return void */ - public function onRouteMessage(ActionEvent $actionEvent); + public function onRouteMessage(ActionEvent $actionEvent): void; } diff --git a/src/Plugin/Router/QueryRouter.php b/src/Plugin/Router/QueryRouter.php index 1f30c61..1829794 100644 --- a/src/Plugin/Router/QueryRouter.php +++ b/src/Plugin/Router/QueryRouter.php @@ -1,21 +1,17 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; -/** - * Class QueryRouter - * - * @package Prooph\ServiceBus\Router - * @author Alexander Miertsch - */ class QueryRouter extends SingleHandlerRouter { } diff --git a/src/Plugin/Router/RegexRouter.php b/src/Plugin/Router/RegexRouter.php index a81153b..cadc33c 100644 --- a/src/Plugin/Router/RegexRouter.php +++ b/src/Plugin/Router/RegexRouter.php @@ -1,37 +1,29 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; use Assert\Assertion; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; use Prooph\ServiceBus\QueryBus; -/** - * Class RegexRouter - * - * @package Prooph\ServiceBus\Router - * @author Alexander Miertsch - */ -class RegexRouter implements MessageBusRouterPlugin, ActionEventListenerAggregate +class RegexRouter extends AbstractPlugin implements MessageBusRouterPlugin { - use DetachAggregateHandlers; - - const ALL = '/.*/'; + public const ALL = '/.*/'; /** * @var array[array[pattern => handler], ...] @@ -48,41 +40,36 @@ class RegexRouter implements MessageBusRouterPlugin, ActionEventListenerAggregat */ public function __construct(array $patternMap = null) { - if (null !== $patternMap) { - foreach ($patternMap as $pattern => $handler) { - if (is_array($handler)) { - foreach ($handler as $singleHandler) { - $this->route($pattern)->to($singleHandler); - } - } else { - $this->route($pattern)->to($handler); + if (null === $patternMap) { + return; + } + + foreach ($patternMap as $pattern => $handler) { + if (is_array($handler)) { + foreach ($handler as $singleHandler) { + $this->route($pattern)->to($singleHandler); } + } else { + $this->route($pattern)->to($handler); } } } - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) + public function attachToMessageBus(MessageBus $messageBus): void { - $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, 'onRouteMessage'], 100)); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); } - /** - * @param string $pattern - * @return $this - * @throws Exception\RuntimeException - */ - public function route($pattern) + public function route(string $pattern): RegexRouter { - Assertion::string($pattern); Assertion::notEmpty($pattern); if (null !== $this->tmpPattern) { - throw new Exception\RuntimeException(sprintf("pattern %s is not mapped to a handler.", $this->tmpPattern)); + throw new Exception\RuntimeException(sprintf('pattern %s is not mapped to a handler.', $this->tmpPattern)); } $this->tmpPattern = $pattern; @@ -92,23 +79,25 @@ public function route($pattern) /** * @param string|object|callable $handler - * @return $this + * * @throws Exception\RuntimeException * @throws Exception\InvalidArgumentException */ - public function to($handler) + public function to($handler): RegexRouter { if (! is_string($handler) && ! is_object($handler) && ! is_callable($handler)) { throw new Exception\InvalidArgumentException(sprintf( - "Invalid handler provided. Expected type is string, object or callable but type of %s given.", + 'Invalid handler provided. Expected type is string, object or callable but type of %s given.', gettype($handler) )); } if (null === $this->tmpPattern) { throw new Exception\RuntimeException(sprintf( - "Cannot map handler %s to a pattern. Please use method route before calling method to", - (is_object($handler))? get_class($handler) : (is_string($handler))? $handler : gettype($handler) + 'Cannot map handler %s to a pattern. Please use method route before calling method to', + is_object($handler) + ? get_class($handler) + : is_string($handler) ? $handler : gettype($handler) )); } @@ -119,10 +108,7 @@ public function to($handler) return $this; } - /** - * @param ActionEvent $actionEvent - */ - public function onRouteMessage(ActionEvent $actionEvent) + public function onRouteMessage(ActionEvent $actionEvent): void { if ($actionEvent->getTarget() instanceof CommandBus || $actionEvent->getTarget() instanceof QueryBus) { $this->onRouteToSingleHandler($actionEvent); @@ -131,22 +117,9 @@ public function onRouteMessage(ActionEvent $actionEvent) } } - /** - * @param ActionEvent $actionEvent - * @deprecated Will be removed with v6.0, use method onRouteMessage instead - */ - public function onRoute(ActionEvent $actionEvent) - { - $this->onRouteMessage($actionEvent); - } - - /** - * @param ActionEvent $actionEvent - * @throws Exception\RuntimeException - */ - private function onRouteToSingleHandler(ActionEvent $actionEvent) + private function onRouteToSingleHandler(ActionEvent $actionEvent): void { - $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); if (empty($messageName)) { return; @@ -159,26 +132,22 @@ private function onRouteToSingleHandler(ActionEvent $actionEvent) if (preg_match($pattern, $messageName)) { if ($alreadyMatched) { throw new Exception\RuntimeException(sprintf( - "Multiple handlers detected for message %s. The patterns %s and %s matches both", + 'Multiple handlers detected for message %s. The patterns %s and %s matches both', $messageName, $alreadyMatched, $pattern )); - } else { - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); - - $alreadyMatched = true; } + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + + $alreadyMatched = true; } } } - /** - * @param ActionEvent $actionEvent - */ - private function onRouteEvent(ActionEvent $actionEvent) + private function onRouteEvent(ActionEvent $actionEvent): void { - $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); if (empty($messageName)) { return; diff --git a/src/Plugin/Router/ServiceLocatorEventRouter.php b/src/Plugin/Router/ServiceLocatorEventRouter.php new file mode 100644 index 0000000..1b14af8 --- /dev/null +++ b/src/Plugin/Router/ServiceLocatorEventRouter.php @@ -0,0 +1,53 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Prooph\ServiceBus\Plugin\Router; + +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; +use Psr\Container\ContainerInterface; + +final class ServiceLocatorEventRouter extends AbstractPlugin implements MessageBusRouterPlugin +{ + /** + * @var ContainerInterface + */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function attachToMessageBus(MessageBus $messageBus): void + { + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); + } + + public function onRouteMessage(ActionEvent $actionEvent): void + { + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if ($this->container->has($messageName)) { + $actionEvent->setParam( + EventBus::EVENT_PARAM_EVENT_LISTENERS, + $this->container->get($messageName) + ); + } + } +} diff --git a/src/Plugin/Router/SingleHandlerRouter.php b/src/Plugin/Router/SingleHandlerRouter.php index b72e998..bd764c0 100644 --- a/src/Plugin/Router/SingleHandlerRouter.php +++ b/src/Plugin/Router/SingleHandlerRouter.php @@ -1,33 +1,25 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin\Router; use Assert\Assertion; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\ServiceBus\Exception; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; -/** - * Class SingleHandlerRouter - * - * @package Prooph\ServiceBus\Router - * @author Alexander Miertsch - */ -class SingleHandlerRouter implements MessageBusRouterPlugin, ActionEventListenerAggregate +class SingleHandlerRouter extends AbstractPlugin implements MessageBusRouterPlugin { - use DetachAggregateHandlers; - /** * @var array[messageName => messageHandler] */ @@ -43,35 +35,30 @@ class SingleHandlerRouter implements MessageBusRouterPlugin, ActionEventListener */ public function __construct(array $messageMap = null) { - if (null !== $messageMap) { - foreach ($messageMap as $messageName => $handler) { - $this->route($messageName)->to($handler); - } + if (null === $messageMap) { + return; + } + + foreach ($messageMap as $messageName => $handler) { + $this->route($messageName)->to($handler); } } - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) + public function attachToMessageBus(MessageBus $messageBus): void { - $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, "onRouteMessage"])); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); } - /** - * @param string $messageName - * @return $this - * @throws Exception\RuntimeException - */ - public function route($messageName) + public function route(string $messageName): SingleHandlerRouter { - Assertion::string($messageName); Assertion::notEmpty($messageName); if (null !== $this->tmpMessageName) { - throw new Exception\RuntimeException(sprintf("Message %s is not mapped to a handler.", $this->tmpMessageName)); + throw new Exception\RuntimeException(sprintf('Message %s is not mapped to a handler.', $this->tmpMessageName)); } $this->tmpMessageName = $messageName; @@ -81,23 +68,27 @@ public function route($messageName) /** * @param string|object|callable $messageHandler - * @return $this + * + * @return SingleHandlerRouter + * * @throws Exception\RuntimeException * @throws Exception\InvalidArgumentException */ - public function to($messageHandler) + public function to($messageHandler): SingleHandlerRouter { if (! is_string($messageHandler) && ! is_object($messageHandler) && ! is_callable($messageHandler)) { throw new Exception\InvalidArgumentException(sprintf( - "Invalid message handler provided. Expected type is string, object or callable but type of %s given.", + 'Invalid message handler provided. Expected type is string, object or callable but type of %s given.', gettype($messageHandler) )); } if (null === $this->tmpMessageName) { throw new Exception\RuntimeException(sprintf( - "Cannot map handler %s to a message. Please use method route before calling method to", - (is_object($messageHandler))? get_class($messageHandler) : (is_string($messageHandler))? $messageHandler : gettype($messageHandler) + 'Cannot map handler %s to a message. Please use method route before calling method to', + is_object($messageHandler) + ? get_class($messageHandler) + : is_string($messageHandler) ? $messageHandler : gettype($messageHandler) )); } @@ -108,18 +99,15 @@ public function to($messageHandler) return $this; } - /** - * @param ActionEvent $actionEvent - */ - public function onRouteMessage(ActionEvent $actionEvent) + public function onRouteMessage(ActionEvent $actionEvent): void { - $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); if (empty($messageName)) { return; } - if (!isset($this->messageMap[$messageName])) { + if (! isset($this->messageMap[$messageName])) { return; } diff --git a/src/Plugin/Router/SingleHandlerServiceLocatorRouter.php b/src/Plugin/Router/SingleHandlerServiceLocatorRouter.php new file mode 100644 index 0000000..f5c752a --- /dev/null +++ b/src/Plugin/Router/SingleHandlerServiceLocatorRouter.php @@ -0,0 +1,52 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Prooph\ServiceBus\Plugin\Router; + +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\AbstractPlugin; +use Psr\Container\ContainerInterface; + +final class SingleHandlerServiceLocatorRouter extends AbstractPlugin implements MessageBusRouterPlugin +{ + /** + * @var ContainerInterface + */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function attachToMessageBus(MessageBus $messageBus): void + { + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + [$this, 'onRouteMessage'], + MessageBus::PRIORITY_ROUTE + ); + } + + public function onRouteMessage(ActionEvent $actionEvent): void + { + $messageName = (string) $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if ($this->container->has($messageName)) { + $actionEvent->setParam( + MessageBus::EVENT_PARAM_MESSAGE_HANDLER, + $this->container->get($messageName) + ); + } + } +} diff --git a/src/Plugin/ServiceLocatorPlugin.php b/src/Plugin/ServiceLocatorPlugin.php index 92a1103..bcd4b43 100644 --- a/src/Plugin/ServiceLocatorPlugin.php +++ b/src/Plugin/ServiceLocatorPlugin.php @@ -1,64 +1,62 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus\Plugin; -use Interop\Container\ContainerInterface; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\MessageBus; +use Psr\Container\ContainerInterface; /** - * Class ServiceLocatorPlugin - * * This plugin can be used to lazy load message handlers. - * Initialize it with a Interop\Container\ContainerInterface + * Initialize it with a Psr\Container\ContainerInterface * and route your messages to the service id only. - * - * @package Prooph\ServiceBus\ServiceLocator - * @author Alexander Miertsch */ -class ServiceLocatorPlugin implements ActionEventListenerAggregate +class ServiceLocatorPlugin extends AbstractPlugin { - use DetachAggregateHandlers; - /** * @var ContainerInterface */ protected $serviceLocator; - /** - * @param ContainerInterface $serviceLocator - */ public function __construct(ContainerInterface $serviceLocator) { $this->serviceLocator = $serviceLocator; } - /** - * @param ActionEventEmitter $events - * - * @return void - */ - public function attach(ActionEventEmitter $events) - { - $this->trackHandler($events->attachListener(MessageBus::EVENT_LOCATE_HANDLER, [$this, 'onLocateMessageHandler'])); - } - public function onLocateMessageHandler(ActionEvent $actionEvent) + public function attachToMessageBus(MessageBus $messageBus): void { - $messageHandlerAlias = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + $this->listenerHandlers[] = $messageBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $messageHandlerAlias = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_string($messageHandlerAlias) && $this->serviceLocator->has($messageHandlerAlias)) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $this->serviceLocator->get($messageHandlerAlias)); + } - if (is_string($messageHandlerAlias) && $this->serviceLocator->has($messageHandlerAlias)) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $this->serviceLocator->get($messageHandlerAlias)); - } + // for event bus only + $eventListeners = []; + foreach ($actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []) as $eventListenerAlias) { + if (is_string($eventListenerAlias) && $this->serviceLocator->has($eventListenerAlias)) { + $eventListeners[] = $this->serviceLocator->get($eventListenerAlias); + } + } + if (! empty($eventListeners)) { + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $eventListeners); + } + }, + MessageBus::PRIORITY_LOCATE_HANDLER + ); } } diff --git a/src/QueryBus.php b/src/QueryBus.php index 6baac25..8894b9d 100644 --- a/src/QueryBus.php +++ b/src/QueryBus.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace Prooph\ServiceBus; use Prooph\Common\Event\ActionEvent; @@ -15,109 +17,95 @@ use Prooph\ServiceBus\Exception\MessageDispatchException; use Prooph\ServiceBus\Exception\RuntimeException; use React\Promise\Deferred; -use React\Promise\Promise; +use React\Promise\PromiseInterface; /** - * Class QueryBus - * * The query bus dispatches a query message to a finder. * The query is maybe dispatched async so the bus returns a promise * which gets either resolved with the response of the finder or rejected with an exception. * Additionally the finder can provide an update status but this is not guaranteed. - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch */ class QueryBus extends MessageBus { - const EVENT_INVOKE_FINDER = 'invoke-finder'; - - const EVENT_PARAM_PROMISE = 'query-promise'; - const EVENT_PARAM_DEFERRED = 'query-deferred'; + public const EVENT_PARAM_PROMISE = 'query-promise'; + public const EVENT_PARAM_DEFERRED = 'query-deferred'; - /** - * Inject an ActionEventDispatcher instance - * - * @param ActionEventEmitter $actionEventDispatcher - * @return void - */ - public function setActionEventEmitter(ActionEventEmitter $actionEventDispatcher) + public function __construct(ActionEventEmitter $actionEventEmitter = null) { - $actionEventDispatcher->attachListener(self::EVENT_INVOKE_FINDER, function (ActionEvent $actionEvent) { - $finder = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); - - if (is_callable($finder)) { - $query = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); - $deferred = $actionEvent->getParam(self::EVENT_PARAM_DEFERRED); - $finder($query, $deferred); - $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); - } - }); - - $this->events = $actionEventDispatcher; + parent::__construct($actionEventEmitter); + + $this->events->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $finder = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_callable($finder)) { + $query = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE); + $deferred = $actionEvent->getParam(self::EVENT_PARAM_DEFERRED); + $finder($query, $deferred); + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLED, true); + } + }, + self::PRIORITY_INVOKE_HANDLER + ); + + $this->events->attachListener( + self::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + throw new RuntimeException(sprintf( + 'QueryBus was not able to identify a Finder for query %s', + $this->getMessageName($actionEvent->getParam(self::EVENT_PARAM_MESSAGE)) + )); + } + }, + self::PRIORITY_LOCATE_HANDLER + ); + + $this->events->attachListener( + self::EVENT_FINALIZE, + function (ActionEvent $actionEvent): void { + if ($exception = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { + $deferred = $actionEvent->getParam(self::EVENT_PARAM_DEFERRED); + $deferred->reject(MessageDispatchException::failed($exception)); + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, null); + } + }, + self::PRIORITY_PROMISE_REJECT + ); } /** * @param mixed $query - * @return Promise + * + * @throws RuntimeException */ - public function dispatch($query) + public function dispatch($query): PromiseInterface { $deferred = new Deferred(); - $promise = $deferred->promise(); + $actionEventEmitter = $this->events; - $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); - - $actionEvent->setTarget($this); - - $actionEvent->setParam(self::EVENT_PARAM_DEFERRED, $deferred); - $actionEvent->setParam(self::EVENT_PARAM_PROMISE, $promise); + $actionEvent = $actionEventEmitter->getNewActionEvent( + self::EVENT_DISPATCH, + $this, + [ + self::EVENT_PARAM_MESSAGE => $query, + self::EVENT_PARAM_DEFERRED => $deferred, + self::EVENT_PARAM_PROMISE => $deferred->promise(), + ] + ); try { - $this->initialize($query, $actionEvent); - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { - $actionEvent->setName(self::EVENT_ROUTE); - $this->trigger($actionEvent); - } - - if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { - throw new RuntimeException(sprintf( - "QueryBus was not able to identify a Finder for query %s", - $this->getMessageName($query) - )); - } - - $finder = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); - - if (is_string($finder) && !is_callable($finder)) { - $actionEvent->setName(self::EVENT_LOCATE_HANDLER); - - $this->trigger($actionEvent); - } - - $actionEvent->setName(self::EVENT_INVOKE_FINDER); - $this->trigger($actionEvent); + $actionEventEmitter->dispatch($actionEvent); if (! $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLED)) { throw new RuntimeException(sprintf('Query %s was not handled', $this->getMessageName($query))); } - + } catch (\Throwable $exception) { + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $exception); + } finally { $this->triggerFinalize($actionEvent); - } catch (\Exception $ex) { - $failedPhase = $actionEvent->getName(); - - $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $ex); - - $this->triggerError($actionEvent); - $this->triggerFinalize($actionEvent); - - //Check if a listener has removed the exception to indicate that it was able to handle it - if ($ex = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { - $actionEvent->setName($failedPhase); - $deferred->reject(MessageDispatchException::failed($actionEvent, $ex)); - } } return $actionEvent->getParam(self::EVENT_PARAM_PROMISE); diff --git a/tests/CommandBusTest.php b/tests/CommandBusTest.php index 46ff956..75a03c5 100644 --- a/tests/CommandBusTest.php +++ b/tests/CommandBusTest.php @@ -1,17 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\Exception\CommandDispatchException; use Prooph\ServiceBus\Exception\MessageDispatchException; @@ -22,11 +24,7 @@ use ProophTest\ServiceBus\Mock\ErrorProducer; use ProophTest\ServiceBus\Mock\MessageHandler; -/** - * Class CommandBusTest - * @package ProophTest\ServiceBus - */ -final class CommandBusTest extends TestCase +class CommandBusTest extends TestCase { /** * @var CommandBus @@ -37,22 +35,30 @@ protected function setUp() { $this->commandBus = new CommandBus(); } + /** * @test */ - public function it_dispatches_a_message_using_the_default_process() + public function it_dispatches_a_message_using_the_default_process(): void { $doSomething = new DoSomething(['todo' => 'buy milk']); $receivedMessage = null; $dispatchEvent = null; - $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, function (DoSomething $doSomething) use (&$receivedMessage) { - $receivedMessage = $doSomething; - }); - - $dispatchEvent = $actionEvent; - }); + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent): void { + $actionEvent->setParam( + MessageBus::EVENT_PARAM_MESSAGE_HANDLER, + function (DoSomething $doSomething) use (&$receivedMessage): void { + $receivedMessage = $doSomething; + } + ); + + $dispatchEvent = $actionEvent; + }, + MessageBus::PRIORITY_ROUTE + ); $this->commandBus->dispatch($doSomething); @@ -63,116 +69,119 @@ public function it_dispatches_a_message_using_the_default_process() /** * @test */ - public function it_triggers_all_defined_action_events() + public function it_triggers_all_defined_action_events(): void { $initializeIsTriggered = false; $detectMessageNameIsTriggered = false; $routeIsTriggered = false; $locateHandlerIsTriggered = false; $invokeHandlerIsTriggered = false; - $handleErrorIsTriggered = false; $finalizeIsTriggered = false; //Should always be triggered - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, - function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered): void { $initializeIsTriggered = true; - } + }, + MessageBus::PRIORITY_INITIALIZE ); //Should be triggered because we dispatch a message that does not //implement Prooph\Common\Messaging\HasMessageName - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_DETECT_MESSAGE_NAME, - function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered): void { $detectMessageNameIsTriggered = true; - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); - } + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, 'custom-message'); + }, + MessageBus::PRIORITY_DETECT_MESSAGE_NAME ); //Should be triggered because we did not provide a message-handler yet - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_ROUTE, - function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$routeIsTriggered): void { $routeIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === 'custom-message') { //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, "error-producer"); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, 'error-producer'); } - } + }, + MessageBus::PRIORITY_ROUTE ); //Should be triggered because we provided the message-handler as string (service id) - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_LOCATE_HANDLER, - function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered): void { $locateHandlerIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === 'error-producer') { $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); } - } + }, + MessageBus::PRIORITY_LOCATE_HANDLER ); //Should be triggered because the message-handler is not callable - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INVOKE_HANDLER, - function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered) { + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered): void { $invokeHandlerIsTriggered = true; $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); if ($handler instanceof ErrorProducer) { $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); } - } - ); - - //Should be triggered because the message-handler threw an exception - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_HANDLE_ERROR, - function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { - $handleErrorIsTriggered = true; - - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); - } - } + }, + MessageBus::PRIORITY_INVOKE_HANDLER ); //Should always be triggered - $this->commandBus->getActionEventEmitter()->attachListener( + $this->commandBus->attach( MessageBus::EVENT_FINALIZE, - function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { - $finalizeIsTriggered = true; - } + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered): void { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { + $finalizeIsTriggered = true; + } + }, + 100 // before exception is thrown ); - $customMessage = new CustomMessage("I have no further meaning"); + $customMessage = new CustomMessage('I have no further meaning'); - $this->commandBus->dispatch($customMessage); + try { + $this->commandBus->dispatch($customMessage); + } catch (CommandDispatchException $exception) { + $this->assertNotNull($exception->getPrevious()); + $this->assertEquals('I can only throw exceptions', $exception->getPrevious()->getMessage()); + } $this->assertTrue($initializeIsTriggered); $this->assertTrue($detectMessageNameIsTriggered); $this->assertTrue($routeIsTriggered); $this->assertTrue($locateHandlerIsTriggered); $this->assertTrue($invokeHandlerIsTriggered); - $this->assertTrue($handleErrorIsTriggered); $this->assertTrue($finalizeIsTriggered); } /** * @test */ - public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name(): void { $handler = new MessageHandler(); - $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { - if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { - $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); - } - }); + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e) use ($handler): void { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + } + }, + MessageBus::PRIORITY_ROUTE + ); - $customMessage = new CustomMessage("foo"); + $customMessage = new CustomMessage('foo'); $this->commandBus->dispatch($customMessage); @@ -181,49 +190,53 @@ public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided /** * @test - * @expectedException \Prooph\ServiceBus\Exception\MessageDispatchException */ - public function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + public function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin(): void { - try { - $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { - throw new \Exception("ka boom"); - }); - - $this->commandBus->dispatch("throw it"); - } catch (MessageDispatchException $e) { - $this->assertInstanceOf(DefaultActionEvent::class, $e->getFailedDispatchEvent()); + $this->expectException(MessageDispatchException::class); + + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function () { + throw new \Exception('ka boom'); + }, + MessageBus::PRIORITY_INITIALIZE + ); - throw $e; - } + $this->commandBus->dispatch('throw it'); } /** * @test - * @expectedException \Prooph\ServiceBus\Exception\RuntimeException */ - public function it_throws_exception_if_event_has_no_handler_after_it_has_been_set_and_event_was_triggered() + public function it_throws_exception_if_event_has_no_handler_after_it_has_been_set_and_event_was_triggered(): void { - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, function (ActionEvent $e) { + $this->expectException(MessageDispatchException::class); + + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, null); - } + }, + MessageBus::PRIORITY_INITIALIZE ); - $this->commandBus->dispatch("throw it"); + $this->commandBus->dispatch('throw it'); } /** * @test - * @expectedException \Prooph\ServiceBus\Exception\RuntimeException */ - public function it_throws_exception_if_message_was_not_handled() + public function it_throws_exception_if_message_was_not_handled(): void { - $this->commandBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, - function (ActionEvent $e) { + $this->expectException(MessageDispatchException::class); + + $this->commandBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new \stdClass()); - } + }, + MessageBus::PRIORITY_INITIALIZE ); $this->commandBus->dispatch('throw it'); @@ -232,21 +245,20 @@ function (ActionEvent $e) { /** * @test */ - public function it_queues_new_commands_as_long_as_it_is_dispatching() + public function it_queues_new_commands_as_long_as_it_is_dispatching(): void { $messageHandler = new MessageHandler(); - $this->commandBus->utilize( - (new CommandRouter()) - ->route(CustomMessage::class)->to($messageHandler) - ->route('initial message')->to(function () use ($messageHandler) { - $delayedMessage = new CustomMessage("delayed message"); + (new CommandRouter()) + ->route(CustomMessage::class)->to($messageHandler) + ->route('initial message')->to(function () use ($messageHandler): void { + $delayedMessage = new CustomMessage('delayed message'); - $this->commandBus->dispatch($delayedMessage); + $this->commandBus->dispatch($delayedMessage); - $this->assertEquals(0, $messageHandler->getInvokeCounter()); - }) - ); + $this->assertEquals(0, $messageHandler->getInvokeCounter()); + }) + ->attachToMessageBus($this->commandBus); $this->commandBus->dispatch('initial message'); @@ -256,21 +268,20 @@ public function it_queues_new_commands_as_long_as_it_is_dispatching() /** * @test */ - public function it_passes_queued_commands_to_command_dispatch_exception_in_case_of_an_error() + public function it_passes_queued_commands_to_command_dispatch_exception_in_case_of_an_error(): void { $messageHandler = new MessageHandler(); - $this->commandBus->utilize( - (new CommandRouter()) - ->route(CustomMessage::class)->to($messageHandler) - ->route('initial message')->to(function () use ($messageHandler) { - $delayedMessage = new CustomMessage("delayed message"); + (new CommandRouter()) + ->route(CustomMessage::class)->to($messageHandler) + ->route('initial message')->to(function () use ($messageHandler): void { + $delayedMessage = new CustomMessage('delayed message'); - $this->commandBus->dispatch($delayedMessage); + $this->commandBus->dispatch($delayedMessage); - throw new \Exception("Ka Boom"); - }) - ); + throw new \Exception('Ka Boom'); + }) + ->attachToMessageBus($this->commandBus); $commandDispatchException = null; diff --git a/tests/Container/BusFactoriesTest.php b/tests/Container/BusFactoriesTest.php index c09092d..75b49e0 100644 --- a/tests/Container/BusFactoriesTest.php +++ b/tests/Container/BusFactoriesTest.php @@ -1,53 +1,54 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Factory; -use Interop\Container\ContainerInterface; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Messaging\Message; use Prooph\Common\Messaging\MessageFactory; use Prooph\ServiceBus\Async\AsyncMessage; use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\Container\AbstractBusFactory; use Prooph\ServiceBus\Container\CommandBusFactory; use Prooph\ServiceBus\Container\EventBusFactory; use Prooph\ServiceBus\Container\QueryBusFactory; +use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception\InvalidArgumentException; +use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Plugin; use Prooph\ServiceBus\Plugin\Router\RegexRouter; use Prooph\ServiceBus\QueryBus; use ProophTest\ServiceBus\Mock\NoopMessageProducer; -use ProophTest\ServiceBus\TestCase; use Prophecy\Argument; +use Psr\Container\ContainerInterface; -/** - * Class BusFactoriesTest - * - * @package ProophTest\ServiceBus\Container - */ -final class BusFactoriesTest extends TestCase +class BusFactoriesTest extends TestCase { /** * @test * @dataProvider provideBuses */ - public function it_creates_a_bus_without_needing_a_application_config($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_bus_without_needing_a_application_config( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $container->has('config')->willReturn(false); $container->has(MessageFactory::class)->willReturn(false); - $bus = $busFactory($container->reveal()); + $bus = $busFactory($container->reveal()); $this->assertInstanceOf($busClass, $bus); } @@ -56,14 +57,17 @@ public function it_creates_a_bus_without_needing_a_application_config($busClass, * @test * @dataProvider provideBuses */ - public function it_creates_a_bus_without_needing_prooph_config($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_bus_without_needing_prooph_config( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $container->has('config')->willReturn(true); $container->get('config')->willReturn([]); $container->has(MessageFactory::class)->willReturn(false); - $bus = $busFactory($container->reveal()); + $bus = $busFactory($container->reveal()); $this->assertInstanceOf($busClass, $bus); } @@ -72,11 +76,14 @@ public function it_creates_a_bus_without_needing_prooph_config($busClass, $busCo * @test * @dataProvider provideBuses */ - public function it_creates_a_new_bus_with_all_plugins_attached_using_a_container_and_configuration($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_new_bus_with_all_plugins_attached_using_a_container_and_configuration( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); - $firstPlugin = $this->prophesize(ActionEventListenerAggregate::class); - $secondPlugin = $this->prophesize(ActionEventListenerAggregate::class); + $firstPlugin = $this->prophesize(Plugin::class); + $secondPlugin = $this->prophesize(Plugin::class); $container->has('config')->willReturn(true); $container->get('config')->willReturn([ @@ -85,16 +92,15 @@ public function it_creates_a_new_bus_with_all_plugins_attached_using_a_container $busConfigKey => [ 'plugins' => [ 'first_plugin_service_id', - 'second_plugin_service_id' - ] - ] - ] - ] + 'second_plugin_service_id', + ], + ], + ], + ], ]); - - $firstPlugin->attach(Argument::type(ActionEventEmitter::class))->shouldBeCalled(); - $secondPlugin->attach(Argument::type(ActionEventEmitter::class))->shouldBeCalled(); + $firstPlugin->attachToMessageBus(Argument::type(MessageBus::class))->shouldBeCalled(); + $secondPlugin->attachToMessageBus(Argument::type(MessageBus::class))->shouldBeCalled(); $container->has('first_plugin_service_id')->willReturn(true); $container->get('first_plugin_service_id')->willReturn($firstPlugin->reveal()); @@ -103,18 +109,22 @@ public function it_creates_a_new_bus_with_all_plugins_attached_using_a_container $container->has(MessageFactory::class)->willReturn(false); - $bus = $busFactory($container->reveal()); + $bus = $busFactory($container->reveal()); $this->assertInstanceOf($busClass, $bus); } /** * @test - * @expectedException \Prooph\ServiceBus\Exception\RuntimeException * @dataProvider provideBuses */ - public function it_throws_a_runtime_exception_if_plugin_is_not_registered_in_container($busClass, $busConfigKey, $busFactory) - { + public function it_throws_a_runtime_exception_if_plugin_is_not_registered_in_container( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { + $this->expectException(RuntimeException::class); + $container = $this->prophesize(ContainerInterface::class); $container->has('config')->willReturn(true); @@ -124,10 +134,10 @@ public function it_throws_a_runtime_exception_if_plugin_is_not_registered_in_con $busConfigKey => [ 'plugins' => [ 'plugin_service_id', - ] - ] - ] - ] + ], + ], + ], + ], ]); $container->has('plugin_service_id')->willReturn(false); @@ -141,8 +151,11 @@ public function it_throws_a_runtime_exception_if_plugin_is_not_registered_in_con * @test * @dataProvider provideBuses */ - public function it_creates_a_bus_with_the_default_router_attached_if_routes_are_configured($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_bus_with_the_default_router_attached_if_routes_are_configured( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); @@ -156,14 +169,14 @@ public function it_creates_a_bus_with_the_default_router_attached_if_routes_are_ $busConfigKey => [ 'router' => [ 'routes' => [ - 'test_message' => function (Message $message) use (&$handlerWasCalled) { + 'test_message' => function (Message $message) use (&$handlerWasCalled): void { $handlerWasCalled = true; - } - ] - ] - ] - ] - ] + }, + ], + ], + ], + ], + ], ]); $container->has(MessageFactory::class)->willReturn(false); @@ -179,8 +192,11 @@ public function it_creates_a_bus_with_the_default_router_attached_if_routes_are_ * @test * @dataProvider provideBuses */ - public function it_creates_a_bus_and_attaches_the_router_defined_via_configuration($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_bus_and_attaches_the_router_defined_via_configuration( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); @@ -195,14 +211,14 @@ public function it_creates_a_bus_and_attaches_the_router_defined_via_configurati 'router' => [ 'type' => RegexRouter::class, 'routes' => [ - '/^test_./' => function (Message $message) use (&$handlerWasCalled) { + '/^test_./' => function (Message $message) use (&$handlerWasCalled): void { $handlerWasCalled = true; - } - ] - ] - ] - ] - ] + }, + ], + ], + ], + ], + ], ]); $container->has(MessageFactory::class)->willReturn(false); @@ -218,8 +234,11 @@ public function it_creates_a_bus_and_attaches_the_router_defined_via_configurati * @test * @dataProvider provideBuses */ - public function it_creates_a_bus_and_attaches_the_message_factory_defined_via_configuration($busClass, $busConfigKey, $busFactory) - { + public function it_creates_a_bus_and_attaches_the_message_factory_defined_via_configuration( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); $messageFactory = $this->prophesize(MessageFactory::class); @@ -235,15 +254,15 @@ public function it_creates_a_bus_and_attaches_the_message_factory_defined_via_co 'router' => [ 'type' => RegexRouter::class, 'routes' => [ - '/^test_./' => function (Message $message) use (&$handlerWasCalled) { + '/^test_./' => function (Message $message) use (&$handlerWasCalled): void { $handlerWasCalled = true; - } - ] + }, + ], ], - 'message_factory' => 'custom_message_factory' - ] - ] - ] + 'message_factory' => 'custom_message_factory', + ], + ], + ], ]); $container->has('custom_message_factory')->willReturn(true); @@ -260,8 +279,11 @@ public function it_creates_a_bus_and_attaches_the_message_factory_defined_via_co * @test * @dataProvider provideBuses */ - public function it_decorates_router_with_async_switch_and_pulls_async_message_producer_from_container($busClass, $busConfigKey, $busFactory) - { + public function it_decorates_router_with_async_switch_and_pulls_async_message_producer_from_container( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); $message->willImplement(AsyncMessage::class); @@ -283,15 +305,15 @@ public function it_decorates_router_with_async_switch_and_pulls_async_message_pr 'async_switch' => 'noop_message_producer', 'type' => RegexRouter::class, 'routes' => [ - '/^test_./' => function (Message $message) use (&$handlerWasCalled) { + '/^test_./' => function (Message $message) use (&$handlerWasCalled): void { $handlerWasCalled = true; - } - ] + }, + ], ], - 'message_factory' => 'custom_message_factory' - ] - ] - ] + 'message_factory' => 'custom_message_factory', + ], + ], + ], ]); $container->has('custom_message_factory')->willReturn(true); @@ -309,35 +331,38 @@ public function it_decorates_router_with_async_switch_and_pulls_async_message_pr * @test * @dataProvider provideBuses */ - public function it_enables_handler_location_by_default($busClass, $busConfigKey, $busFactory) - { + public function it_enables_handler_location_by_default( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); - $message->messageName()->willReturn('test_message'); + $message->messageName()->willReturn('test_message')->shouldBeCalled(); $handlerWasCalled = false; - $container->has('config')->willReturn(true); + $container->has('config')->willReturn(true)->shouldBeCalled(); $container->get('config')->willReturn([ 'prooph' => [ 'service_bus' => [ $busConfigKey => [ 'router' => [ 'routes' => [ - 'test_message' => 'handler_service_id' - ] - ] - ] - ] - ] - ]); + 'test_message' => 'handler_service_id', + ], + ], + ], + ], + ], + ])->shouldBeCalled(); - $container->has('handler_service_id')->willReturn(true); - $container->get('handler_service_id')->willReturn(function (Message $message) use (&$handlerWasCalled) { + $container->has('handler_service_id')->willReturn(true)->shouldBeCalled(); + $container->get('handler_service_id')->willReturn(function (Message $message) use (&$handlerWasCalled): void { $handlerWasCalled = true; - }); + })->shouldBeCalled(); - $container->has(MessageFactory::class)->willReturn(false); + $container->has(MessageFactory::class)->willReturn(false)->shouldBeCalled(); $bus = $busFactory($container->reveal()); @@ -350,8 +375,11 @@ public function it_enables_handler_location_by_default($busClass, $busConfigKey, * @test * @dataProvider provideBuses */ - public function it_provides_possibility_to_disable_handler_location($busClass, $busConfigKey, $busFactory) - { + public function it_provides_possibility_to_disable_handler_location( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); $message = $this->prophesize(Message::class); @@ -364,13 +392,13 @@ public function it_provides_possibility_to_disable_handler_location($busClass, $ $busConfigKey => [ 'router' => [ 'routes' => [ - 'test_message' => 'handler_service_id' - ] + 'test_message' => 'handler_service_id', + ], ], 'enable_handler_location' => false, - ] - ] - ] + ], + ], + ], ]); $container->has(MessageFactory::class)->willReturn(false); @@ -379,9 +407,13 @@ public function it_provides_possibility_to_disable_handler_location($busClass, $ $bus = $busFactory($container->reveal()); - $bus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INVOKE_HANDLER, function (ActionEvent $e) { - $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); - }); + $bus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + MessageBus::PRIORITY_INVOKE_HANDLER + ); $bus->dispatch($message->reveal()); } @@ -390,10 +422,13 @@ public function it_provides_possibility_to_disable_handler_location($busClass, $ * @test * @dataProvider provideBuses */ - public function it_can_handle_application_config_being_of_type_array_access($busClass, $busConfigKey, $busFactory) - { + public function it_can_handle_application_config_being_of_type_array_access( + string $busClass, + string $busConfigKey, + AbstractBusFactory $busFactory + ): void { $container = $this->prophesize(ContainerInterface::class); - $firstPlugin = $this->prophesize(ActionEventListenerAggregate::class); + $firstPlugin = $this->prophesize(Plugin::class); $container->has('config')->willReturn(true); $container->get('config')->willReturn(new \ArrayObject([ @@ -402,21 +437,20 @@ public function it_can_handle_application_config_being_of_type_array_access($bus $busConfigKey => [ 'plugins' => [ 'first_plugin_service_id', - ] - ] - ] - ] + ], + ], + ], + ], ])); - - $firstPlugin->attach(Argument::type(ActionEventEmitter::class))->shouldBeCalled(); + $firstPlugin->attachToMessageBus(Argument::type(MessageBus::class))->shouldBeCalled(); $container->has('first_plugin_service_id')->willReturn(true); $container->get('first_plugin_service_id')->willReturn($firstPlugin->reveal()); $container->has(MessageFactory::class)->willReturn(false); - $bus = $busFactory($container->reveal()); + $bus = $busFactory($container->reveal()); $this->assertInstanceOf($busClass, $bus); } @@ -425,7 +459,7 @@ public function it_can_handle_application_config_being_of_type_array_access($bus * @test * @dataProvider provideBusFactoryClasses */ - public function it_creates_a_bus_from_static_call($busClass, $busFactoryClass) + public function it_creates_a_bus_from_static_call(string $busClass, string $busFactoryClass): void { $container = $this->prophesize(ContainerInterface::class); $container->has('config')->willReturn(true); @@ -433,22 +467,21 @@ public function it_creates_a_bus_from_static_call($busClass, $busFactoryClass) $container->get('config')->willReturn([]); $factory = [$busFactoryClass, 'other_config_id']; - self::assertInstanceOf($busClass, $factory($container->reveal())); + $this->assertInstanceOf($busClass, $factory($container->reveal())); } /** * @test */ - public function it_throws_invalid_argument_exception_without_container_on_static_call() + public function it_throws_invalid_argument_exception_without_container_on_static_call(): void { - $this->setExpectedException( - InvalidArgumentException::class, - 'The first argument must be of type Interop\Container\ContainerInterface' - ); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The first argument must be of type Psr\Container\ContainerInterface'); + CommandBusFactory::other_config_id(); } - public function provideBusFactoryClasses() + public function provideBusFactoryClasses(): array { return [ [CommandBus::class, CommandBusFactory::class], @@ -457,7 +490,7 @@ public function provideBusFactoryClasses() ]; } - public function provideBuses() + public function provideBuses(): array { return [ [CommandBus::class, 'command_bus', new CommandBusFactory()], diff --git a/tests/Container/Plugin/Guard/FinalizeGuardFactoryTest.php b/tests/Container/Plugin/Guard/FinalizeGuardFactoryTest.php index ef702ad..0091549 100644 --- a/tests/Container/Plugin/Guard/FinalizeGuardFactoryTest.php +++ b/tests/Container/Plugin/Guard/FinalizeGuardFactoryTest.php @@ -1,31 +1,30 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Container\Plugin\Guard; -use Interop\Container\ContainerInterface; -use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit\Framework\TestCase; use Prooph\ServiceBus\Container\Plugin\Guard\FinalizeGuardFactory; +use Prooph\ServiceBus\Exception\InvalidArgumentException; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\FinalizeGuard; +use Psr\Container\ContainerInterface; -/** - * Class FinalizeGuardFactoryTest - * @package ProophTest\ServiceBus\Container\Plugin\Guard - */ -final class FinalizeGuardFactoryTest extends TestCase +class FinalizeGuardFactoryTest extends TestCase { /** * @test */ - public function it_creates_route_guard() + public function it_creates_route_guard(): void { $authorizationService = $this->prophesize(AuthorizationService::class); @@ -42,7 +41,7 @@ public function it_creates_route_guard() /** * @test */ - public function it_creates_route_guard_with_exposing_message_name() + public function it_creates_route_guard_with_exposing_message_name(): void { $authorizationService = $this->prophesize(AuthorizationService::class); @@ -56,10 +55,11 @@ public function it_creates_route_guard_with_exposing_message_name() /** * @test - * @expectedException \Prooph\ServiceBus\Exception\InvalidArgumentException */ - public function it_throws_invalid_argument_exception_when_call_static_is_used_without_container() + public function it_throws_invalid_argument_exception_when_call_static_is_used_without_container(): void { + $this->expectException(InvalidArgumentException::class); + FinalizeGuardFactory::{'exposeMessageName'}(); } } diff --git a/tests/Container/Plugin/Guard/RouteGuardFactoryTest.php b/tests/Container/Plugin/Guard/RouteGuardFactoryTest.php index fd8cbea..5df7a9e 100644 --- a/tests/Container/Plugin/Guard/RouteGuardFactoryTest.php +++ b/tests/Container/Plugin/Guard/RouteGuardFactoryTest.php @@ -1,31 +1,30 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Container\Plugin\Guard; -use Interop\Container\ContainerInterface; -use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit\Framework\TestCase; use Prooph\ServiceBus\Container\Plugin\Guard\RouteGuardFactory; +use Prooph\ServiceBus\Exception\InvalidArgumentException; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\RouteGuard; +use Psr\Container\ContainerInterface; -/** - * Class RouteGuardFactoryTest - * @package ProophTest\ServiceBus\Container\Plugin\Guard - */ -final class RouteGuardFactoryTest extends TestCase +class RouteGuardFactoryTest extends TestCase { /** * @test */ - public function it_creates_route_guard() + public function it_creates_route_guard(): void { $authorizationService = $this->prophesize(AuthorizationService::class); @@ -42,7 +41,7 @@ public function it_creates_route_guard() /** * @test */ - public function it_creates_route_guard_with_exposing_message_name() + public function it_creates_route_guard_with_exposing_message_name(): void { $authorizationService = $this->prophesize(AuthorizationService::class); @@ -56,10 +55,11 @@ public function it_creates_route_guard_with_exposing_message_name() /** * @test - * @expectedException \Prooph\ServiceBus\Exception\InvalidArgumentException */ - public function it_throws_invalid_argument_exception_when_call_static_is_used_without_container() + public function it_throws_invalid_argument_exception_when_call_static_is_used_without_container(): void { + $this->expectException(InvalidArgumentException::class); + RouteGuardFactory::{'exposeMessageName'}(); } } diff --git a/tests/EventBusTest.php b/tests/EventBusTest.php index 40d81a5..f78d86c 100644 --- a/tests/EventBusTest.php +++ b/tests/EventBusTest.php @@ -1,17 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception\MessageDispatchException; use Prooph\ServiceBus\MessageBus; @@ -20,11 +22,7 @@ use ProophTest\ServiceBus\Mock\MessageHandler; use ProophTest\ServiceBus\Mock\SomethingDone; -/** - * Class EventBusTest - * @package ProophTest\ServiceBus - */ -final class EventBusTest extends TestCase +class EventBusTest extends TestCase { /** * @var EventBus @@ -35,22 +33,29 @@ protected function setUp() { $this->eventBus = new EventBus(); } + /** * @test */ - public function it_dispatches_a_message_using_the_default_process() + public function it_dispatches_a_message_using_the_default_process(): void { $somethingDone = new SomethingDone(['done' => 'bought milk']); $receivedMessage = null; $dispatchEvent = null; - $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent) { - $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [function (SomethingDone $somethingDone) use (&$receivedMessage) { - $receivedMessage = $somethingDone; - }]); - - $dispatchEvent = $actionEvent; - }); + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent): void { + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [ + function (SomethingDone $somethingDone) use (&$receivedMessage): void { + $receivedMessage = $somethingDone; + }, + ]); + + $dispatchEvent = $actionEvent; + }, + MessageBus::PRIORITY_ROUTE + ); $this->eventBus->dispatch($somethingDone); @@ -61,90 +66,82 @@ public function it_dispatches_a_message_using_the_default_process() /** * @test */ - public function it_triggers_all_defined_action_events() + public function it_triggers_all_defined_action_events(): void { $initializeIsTriggered = false; $detectMessageNameIsTriggered = false; $routeIsTriggered = false; $locateHandlerIsTriggered = false; $invokeHandlerIsTriggered = false; - $handleErrorIsTriggered = false; $finalizeIsTriggered = false; //Should always be triggered - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, - function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered): void { $initializeIsTriggered = true; - } + }, + MessageBus::PRIORITY_INITIALIZE ); //Should be triggered because we dispatch a message that does not //implement Prooph\Common\Messaging\HasMessageName - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_DETECT_MESSAGE_NAME, - function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered): void { $detectMessageNameIsTriggered = true; - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); - } + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, 'custom-message'); + }, + MessageBus::PRIORITY_DETECT_MESSAGE_NAME ); //Should be triggered because we did not provide a message-handler yet - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_ROUTE, - function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$routeIsTriggered): void { $routeIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === 'custom-message') { //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event - $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, ["error-producer"]); + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, ['error-producer']); } - } + }, + MessageBus::PRIORITY_ROUTE ); //Should be triggered because we provided the message-handler as string (service id) - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_LOCATE_HANDLER, - function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered): void { $locateHandlerIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === 'error-producer') { $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); } - } + }, + MessageBus::PRIORITY_LOCATE_HANDLER ); //Should be triggered because the message-handler is not callable - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INVOKE_HANDLER, - function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered) { + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered): void { $invokeHandlerIsTriggered = true; $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); if ($handler instanceof ErrorProducer) { $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); } - } - ); - - //Should be triggered because the message-handler threw an exception - $this->eventBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_HANDLE_ERROR, - function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { - $handleErrorIsTriggered = true; - - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); - } - } + }, + MessageBus::PRIORITY_INVOKE_HANDLER ); //Should always be triggered - $this->eventBus->getActionEventEmitter()->attachListener( + $this->eventBus->attach( MessageBus::EVENT_FINALIZE, - function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered): void { $finalizeIsTriggered = true; } ); - $customMessage = new CustomMessage("I have no further meaning"); + $customMessage = new CustomMessage('I have no further meaning'); $this->eventBus->dispatch($customMessage); @@ -153,24 +150,27 @@ function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { $this->assertTrue($routeIsTriggered); $this->assertTrue($locateHandlerIsTriggered); $this->assertTrue($invokeHandlerIsTriggered); - $this->assertTrue($handleErrorIsTriggered); $this->assertTrue($finalizeIsTriggered); } /** * @test */ - public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name(): void { $handler = new MessageHandler(); - $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { - if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { - $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler]); - } - }); + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e) use ($handler): void { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler]); + } + }, + MessageBus::PRIORITY_ROUTE + ); - $customMessage = new CustomMessage("foo"); + $customMessage = new CustomMessage('foo'); $this->eventBus->dispatch($customMessage); @@ -179,37 +179,40 @@ public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided /** * @test - * @expectedException Prooph\ServiceBus\Exception\MessageDispatchException */ - public function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + public function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin(): void { - try { - $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { - throw new \Exception("ka boom"); - }); - - $this->eventBus->dispatch("throw it"); - } catch (MessageDispatchException $e) { - $this->assertInstanceOf(DefaultActionEvent::class, $e->getFailedDispatchEvent()); + $this->expectException(MessageDispatchException::class); + + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function () { + throw new \Exception('ka boom'); + }, + MessageBus::PRIORITY_INITIALIZE + ); - throw $e; - } + $this->eventBus->dispatch('throw it'); } /** * @test */ - public function it_invokes_all_listeners() + public function it_invokes_all_listeners(): void { $handler = new MessageHandler(); - $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { - if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { - $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler, $handler]); - } - }); + $this->eventBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e) use ($handler): void { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler, $handler]); + } + }, + MessageBus::PRIORITY_ROUTE + ); - $customMessage = new CustomMessage("foo"); + $customMessage = new CustomMessage('foo'); $this->eventBus->dispatch($customMessage); diff --git a/tests/Exception/CommandDispatchExceptionTest.php b/tests/Exception/CommandDispatchExceptionTest.php index e521d71..1a3d8e6 100644 --- a/tests/Exception/CommandDispatchExceptionTest.php +++ b/tests/Exception/CommandDispatchExceptionTest.php @@ -1,39 +1,36 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Exception; -use Prooph\Common\Event\DefaultActionEvent; +use PHPUnit\Framework\TestCase; use Prooph\ServiceBus\Exception\CommandDispatchException; use Prooph\ServiceBus\Exception\MessageDispatchException; -use Prooph\ServiceBus\MessageBus; -use ProophTest\ServiceBus\TestCase; class CommandDispatchExceptionTest extends TestCase { /** * @test */ - public function it_wraps_a_message_dispatch_exception_and_tracks_pending_commands() + public function it_wraps_a_message_dispatch_exception_and_tracks_pending_commands(): void { $pendingCommands = ['dispatchMe', 'tellMe']; - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_INVOKE_HANDLER); - $prevException = new \Exception('previous'); - $messageDispatchException = MessageDispatchException::failed($actionEvent, $prevException); + $messageDispatchException = MessageDispatchException::failed($prevException); $commandDispatchException = CommandDispatchException::wrap($messageDispatchException, $pendingCommands); - $this->assertSame($actionEvent, $commandDispatchException->getFailedDispatchEvent()); $this->assertSame($prevException, $commandDispatchException->getPrevious()); $this->assertSame($pendingCommands, $commandDispatchException->getPendingCommands()); } @@ -41,7 +38,7 @@ public function it_wraps_a_message_dispatch_exception_and_tracks_pending_command /** * @test */ - public function it_can_also_wrap_a_normal_exception() + public function it_can_also_wrap_a_normal_exception(): void { $pendingCommands = ['dispatchMe', 'tellMe']; diff --git a/tests/MessageBusTest.php b/tests/MessageBusTest.php index bad39dd..7cd5a21 100644 --- a/tests/MessageBusTest.php +++ b/tests/MessageBusTest.php @@ -1,69 +1,66 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus; -use Prooph\Common\Event\ActionEventEmitter; +use PHPUnit\Framework\TestCase; use Prooph\ServiceBus\MessageBus; use ProophTest\ServiceBus\Mock\CustomMessageBus; -/** - * Class MessageBusTest - * @package ProophTest\ServiceBus - */ -final class MessageBusTest extends TestCase +class MessageBusTest extends TestCase { /** * @test */ - public function it_attaches_action_event_emitter() + public function it_uses_message_class_as_name_if_no_one_was_set(): void { - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $mock = $actionEventEmitter->reveal(); - $messageBus = new CustomMessageBus(); - $messageBus->setActionEventEmitter($mock); + $messageBus->dispatch(new \stdClass()); - $this->assertSame($mock, $messageBus->getActionEventEmitter()); + $this->assertSame(\stdClass::class, $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); } /** * @test */ - public function it_uses_message_class_as_name_if_no_one_was_set() + public function it_uses_message_as_message_name_if_message_is_a_string(): void { $messageBus = new CustomMessageBus(); - $messageBus->dispatch(new \stdClass()); + $messageBus->dispatch('message and a message name'); - $this->assertSame(\stdClass::class, $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); + $this->assertSame('message and a message name', $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); } /** * @test */ - public function it_uses_message_as_message_name_if_message_is_a_string() + public function it_uses_type_of_message_as_message_name_if_message_is_neither_object_nor_string(): void { $messageBus = new CustomMessageBus(); - $messageBus->dispatch('message and a message name'); + $messageBus->dispatch([]); - $this->assertSame('message and a message name', $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); + $this->assertSame('array', $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); } /** * @test */ - public function it_uses_type_of_message_as_message_name_if_message_is_neither_object_nor_string() + public function it_does_not_attach_to_invalid_event_names(): void { - $messageBus = new CustomMessageBus(); - $messageBus->dispatch([]); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown event name given: invalid'); - $this->assertSame('array', $messageBus->getActionEvent()->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)); + $messageBus = new CustomMessageBus(); + $messageBus->attach('invalid', function () { + }); } } diff --git a/tests/Mock/AsyncCommand.php b/tests/Mock/AsyncCommand.php index b7794f6..7a1fa9d 100644 --- a/tests/Mock/AsyncCommand.php +++ b/tests/Mock/AsyncCommand.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\Command; @@ -18,14 +20,11 @@ class AsyncCommand extends Command implements PayloadConstructable, AsyncMessage { use PayloadTrait; - /** - * @param string $data - * @return AsyncCommand - */ - public static function createCommand($data) + + public static function createCommand(string $data): AsyncCommand { return new self([ - 'data' => $data + 'data' => $data, ]); } } diff --git a/tests/Mock/AsyncEvent.php b/tests/Mock/AsyncEvent.php index e7019aa..2cf90cc 100644 --- a/tests/Mock/AsyncEvent.php +++ b/tests/Mock/AsyncEvent.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\DomainEvent; @@ -15,11 +17,11 @@ use Prooph\Common\Messaging\PayloadTrait; use Prooph\ServiceBus\Async\AsyncMessage; -final class AsyncEvent extends DomainEvent implements PayloadConstructable, AsyncMessage +class AsyncEvent extends DomainEvent implements PayloadConstructable, AsyncMessage { use PayloadTrait; - public static function createEvent($data) + public static function createEvent($data): AsyncEvent { return new self(['data' => $data]); } diff --git a/tests/Mock/CustomMessage.php b/tests/Mock/CustomMessage.php index 53ebfce..24a8e51 100644 --- a/tests/Mock/CustomMessage.php +++ b/tests/Mock/CustomMessage.php @@ -1,29 +1,27 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; -/** - * Class CustomMessage - * @package ProophTest\ServiceBus\Mock - */ final class CustomMessage { private $text; - public function __construct($text) + public function __construct(string $text) { $this->text = $text; } - public function getText() + public function getText(): string { return $this->text; } diff --git a/tests/Mock/CustomMessageBus.php b/tests/Mock/CustomMessageBus.php index 1b3f71a..8e241c5 100644 --- a/tests/Mock/CustomMessageBus.php +++ b/tests/Mock/CustomMessageBus.php @@ -1,43 +1,48 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Event\ActionEvent; use Prooph\ServiceBus\MessageBus; -/** - * Class CustomMessageBus - * @package ProophTest\ServiceBus\Mock - */ -final class CustomMessageBus extends MessageBus +class CustomMessageBus extends MessageBus { private $actionEvent; /** * @param mixed $message */ - public function dispatch($message) + public function dispatch($message): void { - $this->initialize($message, $this->getActionEvent()); + $actionEventEmitter = $this->events; + + $actionEvent = $this->getActionEvent(); + $actionEvent->setName(self::EVENT_DISPATCH); + $actionEvent->setTarget($this); + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE, $message); + + $actionEventEmitter->dispatch($actionEvent); } - public function setActionEvent(ActionEvent $event) + public function setActionEvent(ActionEvent $event): void { $this->actionEvent = $event; } - public function getActionEvent() + public function getActionEvent(): ActionEvent { if (null === $this->actionEvent) { - $this->actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); + $this->actionEvent = $this->events->getNewActionEvent(); } return $this->actionEvent; diff --git a/tests/Mock/CustomMessageCommandHandler.php b/tests/Mock/CustomMessageCommandHandler.php index cf4956f..9c2d9ba 100644 --- a/tests/Mock/CustomMessageCommandHandler.php +++ b/tests/Mock/CustomMessageCommandHandler.php @@ -1,24 +1,22 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; -/** - * Class CustomMessageCommandHandler - * @package ProophTest\ServiceBus\Mock - */ -final class CustomMessageCommandHandler +class CustomMessageCommandHandler { private $lastMessage; - public function handleCustomMessage($message) + public function handle($message): void { $this->lastMessage = $message; } diff --git a/tests/Mock/CustomMessageEventHandler.php b/tests/Mock/CustomMessageEventHandler.php new file mode 100644 index 0000000..becf36f --- /dev/null +++ b/tests/Mock/CustomMessageEventHandler.php @@ -0,0 +1,36 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ProophTest\ServiceBus\Mock; + +final class CustomMessageEventHandler +{ + private $lastMessage; + + private $invokeCounter = 0; + + public function onEvent($message): void + { + $this->lastMessage = $message; + $this->invokeCounter++; + } + + public function getLastMessage() + { + return $this->lastMessage; + } + + public function getInvokeCounter(): int + { + return $this->invokeCounter; + } +} diff --git a/tests/Mock/CustomMessageWithName.php b/tests/Mock/CustomMessageWithName.php index acaa21e..0ecf64b 100644 --- a/tests/Mock/CustomMessageWithName.php +++ b/tests/Mock/CustomMessageWithName.php @@ -1,36 +1,34 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\HasMessageName; -/** - * Class CustomMessage - * @package ProophTest\ServiceBus\Mock - */ -final class CustomMessageWithName implements HasMessageName +class CustomMessageWithName implements HasMessageName { private $text; - public function __construct($text) + public function __construct(string $text) { $this->text = $text; } - public function getText() + public function getText(): string { return $this->text; } - public function messageName() + public function messageName(): string { return 'Prooph\Test\ServiceBus\Mock\CustomMessageWithSomeOtherName'; } diff --git a/tests/Mock/DoSomething.php b/tests/Mock/DoSomething.php index e03296b..f168bf1 100644 --- a/tests/Mock/DoSomething.php +++ b/tests/Mock/DoSomething.php @@ -1,24 +1,22 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\Command; use Prooph\Common\Messaging\PayloadConstructable; use Prooph\Common\Messaging\PayloadTrait; -/** - * Class DoSomething - * @package ProophTest\ServiceBus\Mock - */ -final class DoSomething extends Command implements PayloadConstructable +class DoSomething extends Command implements PayloadConstructable { use PayloadTrait; } diff --git a/tests/Mock/ErrorProducer.php b/tests/Mock/ErrorProducer.php index 838b0e0..09dcfa4 100644 --- a/tests/Mock/ErrorProducer.php +++ b/tests/Mock/ErrorProducer.php @@ -1,23 +1,21 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; -/** - * Class ErrorProducer - * @package ProophTest\ServiceBus\Mock - */ -final class ErrorProducer +class ErrorProducer { public function throwException($message) { - throw new \Exception("I can only throw exceptions"); + throw new \Exception('I can only throw exceptions'); } } diff --git a/tests/Mock/FetchSomething.php b/tests/Mock/FetchSomething.php index b3b8055..5d576ab 100644 --- a/tests/Mock/FetchSomething.php +++ b/tests/Mock/FetchSomething.php @@ -1,24 +1,22 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\PayloadConstructable; use Prooph\Common\Messaging\PayloadTrait; use Prooph\Common\Messaging\Query; -/** - * Class FetchSomething - * @package ProophTest\ServiceBus\Mock - */ -final class FetchSomething extends Query implements PayloadConstructable +class FetchSomething extends Query implements PayloadConstructable { use PayloadTrait; } diff --git a/tests/Mock/Finder.php b/tests/Mock/Finder.php index 7708178..b5e7261 100644 --- a/tests/Mock/Finder.php +++ b/tests/Mock/Finder.php @@ -1,26 +1,24 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; -/** - * Class Finder - * @package ProophTest\ServiceBus\Mock - */ -final class Finder +class Finder { private $message; private $deferred; - public function customMessage($message, $deferred) + public function find($message, $deferred) { $this->message = $message; $this->deferred = $deferred; diff --git a/tests/Mock/MessageHandler.php b/tests/Mock/MessageHandler.php index e27aaf1..ba041e2 100644 --- a/tests/Mock/MessageHandler.php +++ b/tests/Mock/MessageHandler.php @@ -1,44 +1,42 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; -/** - * Class MessageHandler - * @package ProophTest\ServiceBus\Mock - */ -final class MessageHandler +class MessageHandler { private $lastMessage; private $invokeCounter = 0; - public function __invoke($message) + public function __invoke($message): void { $this->lastMessage = $message; $this->invokeCounter++; } - public function handle($message) + public function handle($message): void { $this->lastMessage = $message; $this->invokeCounter++; } - public function onCustomMessage($message) + public function onEvent($message): void { $this->lastMessage = $message; $this->invokeCounter++; } - public function getInvokeCounter() + public function getInvokeCounter(): int { return $this->invokeCounter; } diff --git a/tests/Mock/NonAsyncCommand.php b/tests/Mock/NonAsyncCommand.php index 328cca5..1deae2b 100644 --- a/tests/Mock/NonAsyncCommand.php +++ b/tests/Mock/NonAsyncCommand.php @@ -1,19 +1,14 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/** - * Created by PhpStorm. - * User: GuyRadford - * Date: 28/08/2016 - * Time: 12:07 - */ +declare(strict_types=1); namespace ProophTest\ServiceBus\Mock; @@ -24,14 +19,11 @@ class NonAsyncCommand extends Command implements PayloadConstructable { use PayloadTrait; - /** - * @param string $data - * @return NonAsyncCommand - */ - public static function createCommand($data) + + public static function createCommand(string $data): NonAsyncCommand { return new self([ - 'data' => $data + 'data' => $data, ]); } } diff --git a/tests/Mock/NoopMessageProducer.php b/tests/Mock/NoopMessageProducer.php index ac3fcb8..7673335 100644 --- a/tests/Mock/NoopMessageProducer.php +++ b/tests/Mock/NoopMessageProducer.php @@ -1,13 +1,15 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\Message; @@ -18,12 +20,12 @@ class NoopMessageProducer implements MessageProducer { private $invoked = false; - public function __invoke(Message $message, Deferred $deferred = null) + public function __invoke(Message $message, ?Deferred $deferred = null): void { $this->invoked = true; } - public function isInvoked() + public function isInvoked(): bool { return $this->invoked; } diff --git a/tests/Mock/SomethingDone.php b/tests/Mock/SomethingDone.php index c01b5b4..509b04b 100644 --- a/tests/Mock/SomethingDone.php +++ b/tests/Mock/SomethingDone.php @@ -1,24 +1,22 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Mock; use Prooph\Common\Messaging\DomainEvent; use Prooph\Common\Messaging\PayloadConstructable; use Prooph\Common\Messaging\PayloadTrait; -/** - * Class SomethingDone - * @package ProophTest\ServiceBus\Mock - */ -final class SomethingDone extends DomainEvent implements PayloadConstructable +class SomethingDone extends DomainEvent implements PayloadConstructable { use PayloadTrait; } diff --git a/tests/Plugin/Guard/FinalizeGuardTest.php b/tests/Plugin/Guard/FinalizeGuardTest.php index 472e4d7..7e145bd 100644 --- a/tests/Plugin/Guard/FinalizeGuardTest.php +++ b/tests/Plugin/Guard/FinalizeGuardTest.php @@ -1,177 +1,199 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Guard; -use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\DefaultActionEvent; -use Prooph\Common\Event\ListenerHandler; +use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\Exception\MessageDispatchException; use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\FinalizeGuard; +use Prooph\ServiceBus\Plugin\Guard\UnauthorizedException; use Prooph\ServiceBus\QueryBus; -use React\Promise\Deferred; -/** - * Class FinalizeGuardTest - * @package ProophTest\ServiceBus\Plugin\Guard - */ -final class FinalizeGuardTest extends TestCase +class FinalizeGuardTest extends TestCase { /** - * @test + * @var MessageBus */ - public function it_attaches_to_action_event_emitter() - { - $listenerHandler = $this->prophesize(ListenerHandler::class); - - $authorizationService = $this->prophesize(AuthorizationService::class); - - $routeGuard = new FinalizeGuard($authorizationService->reveal()); + protected $messageBus; - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $actionEventEmitter - ->attachListener(MessageBus::EVENT_FINALIZE, [$routeGuard, 'onFinalize'], -1000) - ->willReturn($listenerHandler->reveal()); - - $routeGuard->attach($actionEventEmitter->reveal()); + protected function setUp(): void + { + $this->messageBus = new QueryBus(); } /** * @test */ - public function it_allows_when_authorization_service_grants_access_without_deferred() + public function it_allows_when_authorization_service_grants_access_without_deferred(): void { $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event')->willReturn(true); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->willReturn(null); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $routeGuard = new FinalizeGuard($authorizationService->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); - $this->assertNull($routeGuard->onFinalize($actionEvent->reveal())); + $this->messageBus->dispatch('test_event'); } /** * @test */ - public function it_allows_when_authorization_service_grants_access_with_deferred() + public function it_allows_when_authorization_service_grants_access_with_deferred(): void { $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event', 'result')->willReturn(true); - $deferred = new Deferred(); - $deferred->resolve('result'); - - $actionEvent = new DefaultActionEvent(QueryBus::EVENT_FINALIZE); - $actionEvent->setParam(QueryBus::EVENT_PARAM_PROMISE, $deferred->promise()); - $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_NAME, 'test_event'); - $routeGuard = new FinalizeGuard($authorizationService->reveal()); - - $routeGuard->onFinalize($actionEvent); - - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->done(); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); + $deferred->resolve('result'); + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + QueryBus::PRIORITY_LOCATE_HANDLER + 1000 + ); + + $promise = $this->messageBus->dispatch('test_event'); + $promise->done(); } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access this resource */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_without_deferred() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_without_deferred(): void { + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access this resource'); + + $this->messageBus = new EventBus(); + $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event')->willReturn(false); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->willReturn(null); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $actionEvent->stopPropagation(true)->willReturn(null); - $routeGuard = new FinalizeGuard($authorizationService->reveal()); - - $routeGuard->onFinalize($actionEvent->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + QueryBus::PRIORITY_LOCATE_HANDLER + 1000 + ); + + try { + $promise = $this->messageBus->dispatch('test_event'); + $promise->done(); + } catch (MessageDispatchException $exception) { + throw $exception->getPrevious(); + } } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access this resource */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_with_deferred() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_with_deferred(): void { + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access this resource'); + $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event', 'result')->willReturn(false); - $deferred = new Deferred(); - $deferred->resolve('result'); - - $actionEvent = new DefaultActionEvent(QueryBus::EVENT_FINALIZE); - $actionEvent->setParam(QueryBus::EVENT_PARAM_PROMISE, $deferred->promise()); - $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_NAME, 'test_event'); - $routeGuard = new FinalizeGuard($authorizationService->reveal()); - - $routeGuard->onFinalize($actionEvent); - - $this->assertTrue($actionEvent->propagationIsStopped()); - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->done(); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); + $deferred->resolve('result'); + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + QueryBus::PRIORITY_LOCATE_HANDLER + 1000 + ); + + try { + $promise = $this->messageBus->dispatch('test_event'); + $promise->done(); + } catch (MessageDispatchException $exception) { + throw $exception->getPrevious(); + } } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access the resource "test_event" */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_without_deferred_and_exposes_message_name() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_without_deferred_and_exposes_message_name(): void { + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access the resource "test_event"'); + + $this->messageBus = new EventBus(); + $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event')->willReturn(false); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->willReturn(null); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $actionEvent->stopPropagation(true)->willReturn(null); - $routeGuard = new FinalizeGuard($authorizationService->reveal(), true); - - $routeGuard->onFinalize($actionEvent->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + QueryBus::PRIORITY_LOCATE_HANDLER + 1000 + ); + + try { + $promise = $this->messageBus->dispatch('test_event'); + $promise->done(); + } catch (MessageDispatchException $exception) { + throw $exception->getPrevious(); + } } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access the resource "test_event" */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_with_deferred_and_exposes_message_name() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_with_deferred_and_exposes_message_name(): void { + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access the resource "test_event"'); + $authorizationService = $this->prophesize(AuthorizationService::class); $authorizationService->isGranted('test_event', 'result')->willReturn(false); - $deferred = new Deferred(); - $deferred->resolve('result'); - - $actionEvent = new DefaultActionEvent(QueryBus::EVENT_FINALIZE); - $actionEvent->setParam(QueryBus::EVENT_PARAM_PROMISE, $deferred->promise()); - $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_NAME, 'test_event'); - - $routeGuard = new FinalizeGuard($authorizationService->reveal(), true); - - $routeGuard->onFinalize($actionEvent); - - $this->assertTrue($actionEvent->propagationIsStopped()); - $actionEvent->getParam(QueryBus::EVENT_PARAM_PROMISE)->done(); + $finalizeGuard = new FinalizeGuard($authorizationService->reveal(), true); + $finalizeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); + $deferred->resolve('result'); + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED, true); + }, + QueryBus::PRIORITY_LOCATE_HANDLER + 1000 + ); + + $promise = $this->messageBus->dispatch('test_event'); + $promise->done(); } } diff --git a/tests/Plugin/Guard/RouteGuardTest.php b/tests/Plugin/Guard/RouteGuardTest.php index 2d40bc7..3f3aa7f 100644 --- a/tests/Plugin/Guard/RouteGuardTest.php +++ b/tests/Plugin/Guard/RouteGuardTest.php @@ -1,102 +1,114 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Guard; -use PHPUnit_Framework_TestCase as TestCase; -use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ListenerHandler; +use PHPUnit\Framework\TestCase; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\Exception\MessageDispatchException; +use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\Guard\AuthorizationService; use Prooph\ServiceBus\Plugin\Guard\RouteGuard; +use Prooph\ServiceBus\Plugin\Guard\UnauthorizedException; -/** - * Class RouteGuardTest - * @package ProophTest\ServiceBus\Plugin\Guard - */ -final class RouteGuardTest extends TestCase +class RouteGuardTest extends TestCase { /** - * @test + * @var CommandBus */ - public function it_attaches_to_action_event_emitter() - { - $listenerHandler = $this->prophesize(ListenerHandler::class); - - $authorizationService = $this->prophesize(AuthorizationService::class); - - $routeGuard = new RouteGuard($authorizationService->reveal()); - - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $actionEventEmitter - ->attachListener(MessageBus::EVENT_ROUTE, [$routeGuard, 'onRoute'], 1000) - ->willReturn($listenerHandler->reveal()); + protected $messageBus; - $routeGuard->attach($actionEventEmitter->reveal()); + protected function setUp(): void + { + $this->messageBus = new CommandBus(); } /** * @test */ - public function it_allows_when_authorization_service_grants_access() + public function it_allows_when_authorization_service_grants_access(): void { - $authorizationService = $this->prophesize(AuthorizationService::class); - $authorizationService->isGranted('test_event', new \stdClass())->willReturn(true); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('CommandBus was not able to identify a CommandHandler for command stdClass'); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)->willReturn(new \stdClass()); + $authorizationService = $this->prophesize(AuthorizationService::class); + $authorizationService->isGranted('stdClass', new \stdClass())->willReturn(true); $routeGuard = new RouteGuard($authorizationService->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); - $this->assertNull($routeGuard->onRoute($actionEvent->reveal())); + try { + $this->messageBus->dispatch(new \stdClass()); + } catch (MessageDispatchException $e) { + throw $e->getPrevious(); + } } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access this resource */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access(): void { - $authorizationService = $this->prophesize(AuthorizationService::class); - $authorizationService->isGranted('test_event', new \stdClass())->willReturn(false); + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access this resource'); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)->willReturn(new \stdClass()); - $actionEvent->stopPropagation(true)->willReturn(null); + $authorizationService = $this->prophesize(AuthorizationService::class); + $authorizationService->isGranted('stdClass', new \stdClass())->willReturn(false); $routeGuard = new RouteGuard($authorizationService->reveal()); - - $routeGuard->onRoute($actionEvent->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + MessageBus::EVENT_DISPATCH, + function () { + throw new \RuntimeException('foo'); + }, + MessageBus::PRIORITY_INVOKE_HANDLER + ); + + try { + $this->messageBus->dispatch(new \stdClass()); + } catch (MessageDispatchException $e) { + throw $e->getPrevious(); + } } /** * @test - * @expectedException \Prooph\ServiceBus\Plugin\Guard\UnauthorizedException - * @expectedExceptionMessage You are not authorized to access the resource "test_event" */ - public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_and_exposed_message_name() + public function it_stops_propagation_and_throws_unauthorizedexception_when_authorization_service_denies_access_and_exposed_message_name(): void { - $authorizationService = $this->prophesize(AuthorizationService::class); - $authorizationService->isGranted('test_event', new \stdClass())->willReturn(false); + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('You are not authorized to access the resource "stdClass"'); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME)->willReturn('test_event'); - $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)->willReturn(new \stdClass()); - $actionEvent->stopPropagation(true)->willReturn(null); + $authorizationService = $this->prophesize(AuthorizationService::class); + $authorizationService->isGranted('stdClass', new \stdClass())->willReturn(false); $routeGuard = new RouteGuard($authorizationService->reveal(), true); - - $routeGuard->onRoute($actionEvent->reveal()); + $routeGuard->attachToMessageBus($this->messageBus); + + $this->messageBus->attach( + MessageBus::EVENT_DISPATCH, + function () { + throw new \RuntimeException('foo'); + }, + MessageBus::PRIORITY_INVOKE_HANDLER + ); + + try { + $this->messageBus->dispatch(new \stdClass()); + } catch (MessageDispatchException $e) { + throw $e->getPrevious(); + } } } diff --git a/tests/Plugin/InvokeStrategy/AbstractInvokeStrategyTest.php b/tests/Plugin/InvokeStrategy/AbstractInvokeStrategyTest.php deleted file mode 100644 index cb7de34..0000000 --- a/tests/Plugin/InvokeStrategy/AbstractInvokeStrategyTest.php +++ /dev/null @@ -1,81 +0,0 @@ - - * (c) 2015-2016 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ProophTest\ServiceBus\Plugin\InvokeStrategy; - -use PHPUnit_Framework_TestCase as TestCase; -use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ListenerHandler; -use Prooph\ServiceBus\MessageBus; -use Prooph\ServiceBus\Plugin\InvokeStrategy\AbstractInvokeStrategy; - -/** - * Class AbstractInvokeStrategyTest - * @package ProophTest\ServiceBus\Plugin\InvokeStrategy - */ -final class AbstractInvokeStrategyTest extends TestCase -{ - /** - * @test - */ - public function it_attached_listener_to_event_and_tracks_it() - { - $strategy = $this->getMockForAbstractClass(AbstractInvokeStrategy::class); - - $listenerHandlerMock = $this->getMockForAbstractClass(ListenerHandler::class); - - $actionEventEmitterMock = $this->getMockForAbstractClass(ActionEventEmitter::class); - $actionEventEmitterMock - ->expects($this->once()) - ->method('attachListener') - ->with(MessageBus::EVENT_INVOKE_HANDLER, $strategy, 0) - ->will($this->returnValue($listenerHandlerMock)); - - $strategy->attach($actionEventEmitterMock); - } - - /** - * @test - */ - public function it_fetches_message_and_handler_and_invokes_them_if_possible() - { - $actionEventMock = $this->getMockForAbstractClass(ActionEvent::class); - $actionEventMock - ->expects($this->at(0)) - ->method('getParam') - ->with(MessageBus::EVENT_PARAM_MESSAGE) - ->will($this->returnValue('message')); - - $actionEventMock - ->expects($this->at(1)) - ->method('getParam') - ->with(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) - ->will($this->returnValue('handler')); - - $actionEventMock->expects($this->at(2)) - ->method('setParam') - ->with(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, true); - - $strategy = $this->getMockForAbstractClass(AbstractInvokeStrategy::class); - $strategy - ->expects($this->once()) - ->method('canInvoke') - ->with('handler', 'message') - ->will($this->returnValue(true)); - - $strategy - ->expects($this->once()) - ->method('invoke') - ->with('handler', 'message'); - - $strategy($actionEventMock); - } -} diff --git a/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php b/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php index 4ead99f..096b268 100644 --- a/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php +++ b/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php @@ -1,82 +1,46 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\InvokeStrategy; -use Prooph\Common\Event\DefaultActionEvent; +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\ActionEvent; use Prooph\ServiceBus\Plugin\InvokeStrategy\FinderInvokeStrategy; use Prooph\ServiceBus\QueryBus; -use ProophTest\ServiceBus\Mock\CustomMessage; -use ProophTest\ServiceBus\Mock\CustomMessageWithName; use ProophTest\ServiceBus\Mock\Finder; -use React\Promise\Deferred; -/** - * Class FinderInvokeStrategyTest - * - * @package ProophTest\ServiceBus\Plugin\InvokeStrategy - */ -final class FinderInvokeStrategyTest extends \PHPUnit_Framework_TestCase +class FinderInvokeStrategyTest extends TestCase { - /** - * @var FinderInvokeStrategy - */ - private $finderInvokeStrategy; - - /** - * @var DefaultActionEvent - */ - private $actionEvent; - - protected function setUp() - { - $this->finderInvokeStrategy = new FinderInvokeStrategy(); - - $this->actionEvent = new DefaultActionEvent(QueryBus::EVENT_INVOKE_FINDER, new QueryBus(), [ - QueryBus::EVENT_PARAM_MESSAGE => new CustomMessage('I am a query'), - QueryBus::EVENT_PARAM_MESSAGE_NAME => CustomMessage::class, - QueryBus::EVENT_PARAM_DEFERRED => new Deferred(), - ]); - } - /** * @test */ - public function it_invokes_a_finder_which_has_method_named_like_the_query() + public function it_invokes_a_finder_which_has_method_named_like_the_query(): void { - $finder = new Finder(); - - $this->actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER, $finder); - - $invokeStrategy = $this->finderInvokeStrategy; - - $invokeStrategy($this->actionEvent); - - $this->assertSame($this->actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE), $finder->getLastMessage()); - $this->assertSame($this->actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED), $finder->getLastDeferred()); - $this->assertTrue($this->actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLED)); - } + $queryBus = new QueryBus(); - /** - * @test - */ - public function it_determines_the_query_name_from_message_name_call_if_event_has_one() - { $finderInvokeStrategy = new FinderInvokeStrategy(); - $customQuery = new CustomMessageWithName("I am an event with a messageName() method"); + $finderInvokeStrategy->attachToMessageBus($queryBus); + + $finder = new Finder(); - $closure = function ($query) { - return $this->determineQueryName($query); - }; - $determineQueryName = $closure->bindTo($finderInvokeStrategy, $finderInvokeStrategy); + $queryBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use ($finder): void { + $actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER, $finder); + }, + QueryBus::PRIORITY_INITIALIZE + ); - $this->assertSame('CustomMessageWithSomeOtherName', $determineQueryName($customQuery)); + $queryBus->dispatch('foo'); + $this->assertEquals('foo', $finder->getLastMessage()); } } diff --git a/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php b/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php index b0b6fe3..00d2c6a 100644 --- a/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php +++ b/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php @@ -1,79 +1,76 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\InvokeStrategy; +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\Plugin\InvokeStrategy\HandleCommandStrategy; use ProophTest\ServiceBus\Mock\CustomMessage; use ProophTest\ServiceBus\Mock\CustomMessageCommandHandler; -use ProophTest\ServiceBus\Mock\CustomMessageWithName; use ProophTest\ServiceBus\Mock\MessageHandler; -use ProophTest\ServiceBus\TestCase; -/** - * Class HandleCommandStrategyTest - * - * @package ProophTest\ServiceBus\Plugin\InvokeStrategy - * @author Alexander Miertsch - */ class HandleCommandStrategyTest extends TestCase { /** * @test */ - public function it_invokes_the_handle_command_method_of_the_handler() + public function it_invokes_the_handle_command_method_of_the_handler(): void { - $handleCommandStrategy = new HandleCommandStrategy(); + $commandHandler = new MessageHandler(); - $doSomething = new CustomMessage("I am a command"); + $commandBus = new CommandBus(); + $commandBus->attach( + CommandBus::EVENT_DISPATCH, + function (ActionEvent $event) use ($commandHandler): void { + $event->setParam(CommandBus::EVENT_PARAM_MESSAGE_HANDLER, $commandHandler); + }, + CommandBus::PRIORITY_ROUTE + ); - $handleCommandHandler = new MessageHandler(); + $handleCommandStrategy = new HandleCommandStrategy(); + $handleCommandStrategy->attachToMessageBus($commandBus); - $this->assertTrue($handleCommandStrategy->canInvoke($handleCommandHandler, $doSomething)); + $doSomething = new CustomMessage('I am a command'); - $handleCommandStrategy->invoke($handleCommandHandler, $doSomething); + $commandBus->dispatch($doSomething); - $this->assertSame($doSomething, $handleCommandHandler->getLastMessage()); + $this->assertSame($doSomething, $commandHandler->getLastMessage()); } /** * @test */ - public function it_invokes_the_handle_command_method_of_the_handler_without_command_name() + public function it_invokes_the_handle_command_method_of_the_handler_without_command_name(): void { - $handleCommandStrategy = new HandleCommandStrategy(); - - $doSomething = new CustomMessage("I am a command"); - - $handleCommandHandler = new CustomMessageCommandHandler(); + $commandHandler = new CustomMessageCommandHandler(); - $this->assertTrue($handleCommandStrategy->canInvoke($handleCommandHandler, $doSomething)); + $commandBus = new CommandBus(); + $commandBus->attach( + CommandBus::EVENT_DISPATCH, + function (ActionEvent $event) use ($commandHandler): void { + $event->setParam(CommandBus::EVENT_PARAM_MESSAGE_HANDLER, $commandHandler); + }, + CommandBus::PRIORITY_ROUTE + ); - $handleCommandStrategy->invoke($handleCommandHandler, $doSomething); - - $this->assertSame($doSomething, $handleCommandHandler->getLastMessage()); - } - - /** - * @test - */ - public function it_determines_the_command_name_from_message_name_call_if_event_has_one() - { $handleCommandStrategy = new HandleCommandStrategy(); - $customCommand = new CustomMessageWithName("I am an event with a messageName() method"); + $handleCommandStrategy->attachToMessageBus($commandBus); + + $doSomething = new CustomMessage('I am a command'); - $closure = function ($command) { - return $this->determineCommandName($command); - }; - $determineCommandName = $closure->bindTo($handleCommandStrategy, $handleCommandStrategy); + $commandBus->dispatch($doSomething); - $this->assertSame('CustomMessageWithSomeOtherName', $determineCommandName($customCommand)); + $this->assertSame($doSomething, $commandHandler->getLastMessage()); } } diff --git a/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php b/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php index d726536..e2fa813 100644 --- a/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php +++ b/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php @@ -1,60 +1,69 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\InvokeStrategy; +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\DefaultListenerHandler; +use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Plugin\InvokeStrategy\OnEventStrategy; +use Prooph\ServiceBus\Plugin\Router\EventRouter; use ProophTest\ServiceBus\Mock\CustomMessage; -use ProophTest\ServiceBus\Mock\CustomMessageWithName; -use ProophTest\ServiceBus\Mock\MessageHandler; -use ProophTest\ServiceBus\TestCase; +use ProophTest\ServiceBus\Mock\CustomMessageEventHandler; +use Prophecy\Argument; -/** - * Class OnEventStrategyTest - * - * @package ProophTest\ServiceBus\Plugin\InvokeStrategy - * @author Alexander Miertsch - */ class OnEventStrategyTest extends TestCase { /** * @test */ - public function it_invokes_the_on_event_method_of_the_handler() + public function it_invokes_the_on_event_method_of_the_handler(): void { - $onEventStrategy = new OnEventStrategy(); + $eventBus = new EventBus(); - $customEvent = new CustomMessage("I am an event"); + $onEventStrategy = new OnEventStrategy(); + $onEventStrategy->attachToMessageBus($eventBus); - $onEventHandler = new MessageHandler(); + $onEventHandler = new CustomMessageEventHandler(); - $this->assertTrue($onEventStrategy->canInvoke($onEventHandler, $customEvent)); + $eventRouter = new EventRouter([ + 'ProophTest\ServiceBus\Mock\CustomMessage' => $onEventHandler, + ]); + $eventRouter->attachToMessageBus($eventBus); - $onEventStrategy->invoke($onEventHandler, $customEvent); + $customEvent = new CustomMessage('I am an event'); + $eventBus->dispatch($customEvent); $this->assertSame($customEvent, $onEventHandler->getLastMessage()); + $this->assertSame(1, $onEventHandler->getInvokeCounter()); } /** * @test */ - public function it_determines_the_event_name_from_message_name_call_if_event_has_one() + public function it_can_be_attached_to_event_bus(): void { $onEventStrategy = new OnEventStrategy(); - $customEvent = new CustomMessageWithName("I am an event with a messageName() method"); - $closure = function ($event) { - return $this->determineEventName($event); - }; - $determineEventName = $closure->bindTo($onEventStrategy, $onEventStrategy); + $bus = $this->prophesize(EventBus::class); + $bus->attach(Argument::type('string'), Argument::type('callable'), Argument::type('integer')) + ->shouldBeCalled() + ->willReturn( + new DefaultListenerHandler( + function () { + } + ) + ); - $this->assertSame('CustomMessageWithSomeOtherName', $determineEventName($customEvent)); + $onEventStrategy->attachToMessageBus($bus->reveal()); } } diff --git a/tests/Plugin/MessageFactoryPluginTest.php b/tests/Plugin/MessageFactoryPluginTest.php index 8f2960e..268d6a6 100644 --- a/tests/Plugin/MessageFactoryPluginTest.php +++ b/tests/Plugin/MessageFactoryPluginTest.php @@ -1,81 +1,93 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin; -use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\DefaultActionEvent; use Prooph\Common\Messaging\MessageFactory; use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Exception\MessageDispatchException; use Prooph\ServiceBus\Plugin\MessageFactoryPlugin; use ProophTest\ServiceBus\Mock\DoSomething; use Prophecy\Argument; -/** - * Class MessageFactoryPluginTest - * @package ProophTest\ServiceBus\Plugin - */ -final class MessageFactoryPluginTest extends TestCase +class MessageFactoryPluginTest extends TestCase { /** * @test */ - public function it_turns_a_message_given_as_array_into_a_message_object_using_a_factory() + public function it_turns_a_message_given_as_array_into_a_message_object_using_a_factory(): void { + $commandBus = new CommandBus(); $messageFactory = $this->prophesize(MessageFactory::class); - $messageFactory->createMessageFromArray("custom-message", Argument::any())->will(function ($args) { + $messageFactory->createMessageFromArray('custom-message', Argument::any())->will(function ($args): DoSomething { list($messageName, $messageArr) = $args; return new DoSomething($messageArr['payload']); }); $factoryPlugin = new MessageFactoryPlugin($messageFactory->reveal()); + $factoryPlugin->attachToMessageBus($commandBus); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_INITIALIZE, new CommandBus(), [ - //We provide message as array containing a "message_name" key because only in this case the factory plugin - //gets active - MessageBus::EVENT_PARAM_MESSAGE => [ - 'message_name' => 'custom-message', - 'payload' => ["some data"] - ] - ]); - - $factoryPlugin($actionEvent); + $commandBus->attach( + CommandBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent): void { + $message = $actionEvent->getParam(CommandBus::EVENT_PARAM_MESSAGE); + $this->assertInstanceOf(DoSomething::class, $message); + $this->assertEquals(['some data'], $message->payload()); + } + ); - $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); - - $this->assertInstanceOf(DoSomething::class, $message); - $this->assertEquals(["some data"], $message->payload()); + try { + $commandBus->dispatch([ + 'message_name' => 'custom-message', + 'payload' => ['some data'], + ]); + } catch (MessageDispatchException $exception) { + // ignore + } } /** * @test */ - public function it_will_return_eary_if_message_name_not_present_in_message() + public function it_will_return_eary_if_message_name_not_present_in_message(): void { - $messageFactoryMock = $this->getMockForAbstractClass(MessageFactory::class); - $messageFactoryMock - ->expects($this->never()) - ->method('createMessageFromArray'); + $commandBus = new CommandBus(); + $messageFactory = $this->prophesize(MessageFactory::class); + + $factoryPlugin = new MessageFactoryPlugin($messageFactory->reveal()); + $factoryPlugin->attachToMessageBus($commandBus); - $actionEventMock = $this->getMockForAbstractClass(ActionEvent::class); - $actionEventMock - ->expects($this->once()) - ->method('getParam') - ->with(MessageBus::EVENT_PARAM_MESSAGE) - ->will($this->returnValue([])); + $commandBus->attach( + CommandBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent): void { + $message = $actionEvent->getParam(CommandBus::EVENT_PARAM_MESSAGE); + $this->assertEquals( + [ + 'payload' => ['some data'], + ], + $message); + } + ); - $messagePlugin = new MessageFactoryPlugin($messageFactoryMock); - $messagePlugin($actionEventMock); + try { + $commandBus->dispatch([ + 'payload' => ['some data'], + ]); + } catch (MessageDispatchException $exception) { + // ignore + } } } diff --git a/tests/Plugin/MessageProducerPluginTest.php b/tests/Plugin/MessageProducerPluginTest.php index 170b58c..3b55034 100644 --- a/tests/Plugin/MessageProducerPluginTest.php +++ b/tests/Plugin/MessageProducerPluginTest.php @@ -1,90 +1,79 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventEmitter; -use Prooph\Common\Event\ListenerHandler; use Prooph\ServiceBus\Async\MessageProducer; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\MessageProducerPlugin; -use ProophTest\ServiceBus\TestCase; +use ProophTest\ServiceBus\Mock\DoSomething; +use ProophTest\ServiceBus\Mock\SomethingDone; +use Prophecy\Argument; -/** - * Class MessageProducerPluginTest - * - * @package ProophTest\ServiceBus\Plugin - */ -final class MessageProducerPluginTest extends TestCase +class MessageProducerPluginTest extends TestCase { /** * @test */ - public function it_sets_message_producer_as_message_handler_on_dispatch_initialize() + public function it_sets_message_producer_as_message_handler_on_dispatch_initialize(): void { + $command = new DoSomething(['foo' => 'bar']); + $messageProducer = $this->prophesize(MessageProducer::class); - $commandBus = $this->prophesize(CommandBus::class); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $listenerHandler = $this->prophesize(ListenerHandler::class); + $messageProducer->__invoke(Argument::type(DoSomething::class))->shouldBeCalled(); + $commandBus = new CommandBus(); $messageProducerPlugin = new MessageProducerPlugin($messageProducer->reveal()); + $messageProducerPlugin->attachToMessageBus($commandBus); - $actionEventEmitter - ->attachListener(MessageBus::EVENT_INITIALIZE, [$messageProducerPlugin, 'onDispatchInitialize']) - ->willReturn($listenerHandler->reveal()) - ->shouldBeCalled(); - - $messageProducerPlugin->attach($actionEventEmitter->reveal()); + $handler = null; - $actionEvent->getTarget()->willReturn($commandBus->reveal()); + $commandBus->attach( + CommandBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$handler): void { + $handler = $actionEvent->getParam(CommandBus::EVENT_PARAM_MESSAGE_HANDLER); + } + ); - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $messageProducer->reveal())->shouldBeCalled(); - - $messageProducerPlugin->onDispatchInitialize($actionEvent->reveal()); + $commandBus->dispatch($command); + $this->assertSame($messageProducer->reveal(), $handler); } /** * @test */ - public function it_adds_message_producer_as_event_listener_on_dispatch_initialize() + public function it_adds_message_producer_as_event_listener_on_dispatch_initialize(): void { + $event = new SomethingDone(['foo' => 'bar']); + $messageProducer = $this->prophesize(MessageProducer::class); - $eventBus = $this->prophesize(EventBus::class); - $actionEvent = $this->prophesize(ActionEvent::class); - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $listenerHandler = $this->prophesize(ListenerHandler::class); + $eventBus = new EventBus(); $messageProducerPlugin = new MessageProducerPlugin($messageProducer->reveal()); + $messageProducerPlugin->attachToMessageBus($eventBus); - $actionEventEmitter - ->attachListener(MessageBus::EVENT_INITIALIZE, [$messageProducerPlugin, 'onDispatchInitialize']) - ->willReturn($listenerHandler->reveal()) - ->shouldBeCalled(); - - $messageProducerPlugin->attach($actionEventEmitter->reveal()); - - $actionEvent->getTarget()->willReturn($eventBus->reveal()); - - $eventListeners = ['i_am_an_event_listener']; - - $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [])->willReturn($eventListeners); - - //Message Producer should be added to list of event listeners - $eventListeners[] = $messageProducer->reveal(); + $listeners = null; - $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $eventListeners)->shouldBeCalled(); + $eventBus->attach( + EventBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$listeners): void { + $listeners = $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS); + } + ); - $messageProducerPlugin->onDispatchInitialize($actionEvent->reveal()); + $eventBus->dispatch($event); + $this->assertSame($messageProducer->reveal(), $listeners[0]); } } diff --git a/tests/Plugin/Router/AsyncSwitchMessageRouterTest.php b/tests/Plugin/Router/AsyncSwitchMessageRouterTest.php index 1b53dc6..03f19cf 100644 --- a/tests/Plugin/Router/AsyncSwitchMessageRouterTest.php +++ b/tests/Plugin/Router/AsyncSwitchMessageRouterTest.php @@ -1,18 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Router; -use Prooph\Common\Event\ActionEventEmitter; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\DefaultActionEvent; -use Prooph\Common\Event\ListenerHandler; use Prooph\ServiceBus\Async\MessageProducer; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; @@ -23,55 +24,47 @@ use ProophTest\ServiceBus\Mock\AsyncCommand; use ProophTest\ServiceBus\Mock\AsyncEvent; use ProophTest\ServiceBus\Mock\NonAsyncCommand; -use ProophTest\ServiceBus\TestCase; -/** - * Class SingleHandlerRouterTest - * - * @package ProophTest\ServiceBus\Plugin\Router - * @author Alexander Miertsch - */ class AsyncSwitchMessageRouterTest extends TestCase { - /** * @test */ - public function it_sets_message_producer_as_message_handler_on_dispatch_initialize() + public function it_sets_message_producer_as_message_handler_on_dispatch_initialize(): void { - $actionEventEmitter = $this->prophesize(ActionEventEmitter::class); - $listenerHandler = $this->prophesize(ListenerHandler::class); - $messageProducer = $this->prophesize(MessageProducer::class); + $commandBus = new CommandBus(); + $router = new AsyncSwitchMessageRouter(new SingleHandlerRouter(), $messageProducer->reveal()); + $router->attachToMessageBus($commandBus); - $actionEventEmitter - ->attachListener(MessageBus::EVENT_ROUTE, [$router, 'onRouteMessage']) - ->willReturn($listenerHandler->reveal()) - ->shouldBeCalled(); + $message = new AsyncCommand(['foo' => 'bar']); - $router->attach($actionEventEmitter->reveal()); + $commandBus->dispatch($message); } - /** * @test */ - public function it_returns_early_when_message_name_is_empty() + public function it_returns_early_when_message_name_is_empty(): void { $messageProducer = $this->prophesize(MessageProducer::class); $router = new AsyncSwitchMessageRouter(new SingleHandlerRouter(), $messageProducer->reveal()); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_INITIALIZE, new CommandBus(), [ - //We provide message as array containing a "message_name" key because only in this case the factory plugin - //gets active - MessageBus::EVENT_PARAM_MESSAGE => [ - 'message_name' => 'custom-message', - 'payload' => ["some data"] + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + //We provide message as array containing a "message_name" key because only in this case the factory plugin + //gets active + MessageBus::EVENT_PARAM_MESSAGE => [ + 'message_name' => 'custom-message', + 'payload' => ['some data'], + ], ] - ]); + ); $router->onRouteMessage($actionEvent); @@ -92,23 +85,22 @@ public function unmarked_message_is_passed_to_decorated_router() new CommandBus(), [ MessageBus::EVENT_PARAM_MESSAGE_NAME => get_class($message), - MessageBus::EVENT_PARAM_MESSAGE => $message + MessageBus::EVENT_PARAM_MESSAGE => $message, ] ); - $decoratedRouter->onRouteMessage($actionEvent)->willReturn('handled-by-decorated-router'); + $decoratedRouter->onRouteMessage($actionEvent)->shouldBeCalled(); $router = new AsyncSwitchMessageRouter($decoratedRouter->reveal(), $messageProducer->reveal()); - $rtn = $router->onRouteMessage($actionEvent); + $router->onRouteMessage($actionEvent); - $this->assertEquals('handled-by-decorated-router', $rtn); $updatedMessage = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); $this->assertArrayNotHasKey('handled-async', $updatedMessage->metadata()); } /** - * @test - */ + * @test + */ public function marked_message_is_passed_to_async_producer() { $messageProducer = $this->prophesize(MessageProducer::class); @@ -119,7 +111,7 @@ public function marked_message_is_passed_to_async_producer() new CommandBus(), [ MessageBus::EVENT_PARAM_MESSAGE_NAME => get_class($message), - MessageBus::EVENT_PARAM_MESSAGE => $message + MessageBus::EVENT_PARAM_MESSAGE => $message, ] ); @@ -149,16 +141,15 @@ public function marked_message_is_passed_to_decorated_router_as_already_handled_ new CommandBus(), [ MessageBus::EVENT_PARAM_MESSAGE_NAME => get_class($message), - MessageBus::EVENT_PARAM_MESSAGE => $message + MessageBus::EVENT_PARAM_MESSAGE => $message, ] ); - $decoratedRouter->onRouteMessage($actionEvent)->willReturn('handled-by-decorated-router'); + $decoratedRouter->onRouteMessage($actionEvent)->shouldBeCalled(); $router = new AsyncSwitchMessageRouter($decoratedRouter->reveal(), $messageProducer->reveal()); - $rtn = $router->onRouteMessage($actionEvent); + $router->onRouteMessage($actionEvent); - $this->assertEquals('handled-by-decorated-router', $rtn); $updatedMessage = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); $this->assertArrayHasKey('handled-async', $updatedMessage->metadata()); $this->assertTrue($updatedMessage->metadata()['handled-async']); @@ -167,17 +158,18 @@ public function marked_message_is_passed_to_decorated_router_as_already_handled_ /** * @test */ - public function it_sets_message_producer_as_event_listener_if_target_is_an_event_bus() + public function it_sets_message_producer_as_event_listener_if_target_is_an_event_bus(): void { $messageProducer = $this->prophesize(MessageProducer::class); $message = AsyncEvent::createEvent('test-data'); + $actionEvent = new DefaultActionEvent( - MessageBus::EVENT_ROUTE, + MessageBus::EVENT_DISPATCH, new EventBus(), [ MessageBus::EVENT_PARAM_MESSAGE_NAME => get_class($message), - MessageBus::EVENT_PARAM_MESSAGE => $message + MessageBus::EVENT_PARAM_MESSAGE => $message, ] ); diff --git a/tests/Plugin/Router/EventRouterTest.php b/tests/Plugin/Router/EventRouterTest.php index 40d1c1d..468b18e 100644 --- a/tests/Plugin/Router/EventRouterTest.php +++ b/tests/Plugin/Router/EventRouterTest.php @@ -1,70 +1,72 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Router; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\Exception\InvalidArgumentException; +use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\Router\EventRouter; -use ProophTest\ServiceBus\TestCase; -/** - * Class EventRouterTest - * - * @package ProophTest\ServiceBus\Plugin\Router - * @author Alexander Miertsch - */ class EventRouterTest extends TestCase { /** * @test */ - public function it_can_handle_routing_definition_by_chaining_route_to() + public function it_can_handle_routing_definition_by_chaining_route_to(): void { $router = new EventRouter(); - $router->route('SomethingDone')->to("SomethingDoneListener1")->andTo('SomethingDoneListener2'); - - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', - ]); + $router->route('SomethingDone')->to('SomethingDoneListener1')->andTo('SomethingDoneListener2'); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ], + MessageBus::PRIORITY_ROUTE + ); $router->onRouteMessage($actionEvent); $listeners = $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS); $this->assertCount(2, $listeners); - $this->assertEquals("SomethingDoneListener1", $listeners[0]); - $this->assertEquals("SomethingDoneListener2", $listeners[1]); + $this->assertEquals('SomethingDoneListener1', $listeners[0]); + $this->assertEquals('SomethingDoneListener2', $listeners[1]); } /** * @test */ - public function it_fails_on_routing_a_second_event_before_first_event_is_routed_at_least_to_one_listener() + public function it_fails_on_routing_a_second_event_before_first_event_is_routed_at_least_to_one_listener(): void { + $this->expectException(RuntimeException::class); + $router = new EventRouter(); $router->route('SomethingDone'); - $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); - $router->route('AnotherEvent'); } /** * @test */ - public function it_can_route_a_second_event_after_the_first_one_is_routed_to_at_least_one_listener() + public function it_can_route_a_second_event_after_the_first_one_is_routed_to_at_least_one_listener(): void { $router = new EventRouter(); @@ -79,21 +81,22 @@ public function it_can_route_a_second_event_after_the_first_one_is_routed_to_at_ /** * @test */ - public function it_fails_on_setting_a_listener_before_an_event_is_set() + public function it_fails_on_setting_a_listener_before_an_event_is_set(): void { - $router = new EventRouter(); + $this->expectException(RuntimeException::class); - $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); + $router = new EventRouter(); $router->to('SomethingDoneListener'); } /** * @test - * @expectedException \Prooph\ServiceBus\Exception\InvalidArgumentException */ - public function it_fails_on_setting_an_invalid_listener() + public function it_fails_on_setting_an_invalid_listener(): void { + $this->expectException(InvalidArgumentException::class); + $router = new EventRouter(); $router->to(null); } @@ -101,71 +104,91 @@ public function it_fails_on_setting_an_invalid_listener() /** * @test */ - public function it_takes_a_routing_definition_with_a_single_listener_on_instantiation() + public function it_takes_a_routing_definition_with_a_single_listener_on_instantiation(): void { $router = new EventRouter([ - 'SomethingDone' => 'SomethingDoneListener' + 'SomethingDone' => 'SomethingDoneListener', ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ], + MessageBus::PRIORITY_ROUTE + ); $router->onRouteMessage($actionEvent); - $this->assertEquals("SomethingDoneListener", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); + $this->assertEquals('SomethingDoneListener', $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); } /** * @test */ - public function it_takes_a_routing_definition_with_a_multiple_listeners_on_instantiation() + public function it_takes_a_routing_definition_with_a_multiple_listeners_on_instantiation(): void { $router = new EventRouter([ - 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] + 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ], + MessageBus::PRIORITY_ROUTE + ); $router->onRouteMessage($actionEvent); - $this->assertEquals("SomethingDoneListener1", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); - $this->assertEquals("SomethingDoneListener2", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[1]); + $this->assertEquals('SomethingDoneListener1', $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); + $this->assertEquals('SomethingDoneListener2', $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[1]); } /** * @test */ - public function it_still_works_if_deprecated_method_on_route_event_is_used() + public function it_still_works_if_deprecated_method_on_route_event_is_used(): void { $router = new EventRouter([ - 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] + 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ], + MessageBus::PRIORITY_ROUTE + ); - $router->onRouteEvent($actionEvent); + $router->onRouteMessage($actionEvent); - $this->assertEquals("SomethingDoneListener1", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); - $this->assertEquals("SomethingDoneListener2", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[1]); + $this->assertEquals('SomethingDoneListener1', $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); + $this->assertEquals('SomethingDoneListener2', $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[1]); } /** * @test */ - public function it_returns_early_on_route_event_when_message_name_is_empty() + public function it_returns_early_on_route_event_when_message_name_is_empty(): void { $router = new EventRouter([ - 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] + 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - '' => 'SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + '' => 'SomethingDone', + ], + MessageBus::PRIORITY_ROUTE + ); $router->onRouteMessage($actionEvent); @@ -176,15 +199,20 @@ public function it_returns_early_on_route_event_when_message_name_is_empty() /** * @test */ - public function it_returns_early_on_route_event_when_message_name_is_not_in_event_map() + public function it_returns_early_on_route_event_when_message_name_is_not_in_event_map(): void { $router = new EventRouter([ - 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] + 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'unknown', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'unknown', + ], + MessageBus::PRIORITY_ROUTE + ); $router->onRouteMessage($actionEvent); diff --git a/tests/Plugin/Router/RegexRouterTest.php b/tests/Plugin/Router/RegexRouterTest.php index a6b313d..7aa16ac 100644 --- a/tests/Plugin/Router/RegexRouterTest.php +++ b/tests/Plugin/Router/RegexRouterTest.php @@ -1,60 +1,67 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Router; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\Exception\InvalidArgumentException; +use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\Router\RegexRouter; -use ProophTest\ServiceBus\TestCase; -/** - * Class RegexRouterTest - * - * @package ProophTest\ServiceBus\Plugin\Router - * @author Alexander Miertsch - */ class RegexRouterTest extends TestCase { /** * @test */ - public function it_matches_pattern_with_command_name_to_detect_appropriate_handler() + public function it_matches_pattern_with_command_name_to_detect_appropriate_handler(): void { $regexRouter = new RegexRouter(); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to("DoSomethingHandler"); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to('DoSomethingHandler'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ], + MessageBus::PRIORITY_ROUTE + ); $regexRouter->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $this->assertEquals('DoSomethingHandler', $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } /** * @test */ - public function it_returns_early_when_message_name_is_empty() + public function it_returns_early_when_message_name_is_empty(): void { $regexRouter = new RegexRouter(); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to("DoSomethingHandler"); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to('DoSomethingHandler'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - '' => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + '' => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $regexRouter->onRouteMessage($actionEvent); @@ -64,18 +71,22 @@ public function it_returns_early_when_message_name_is_empty() /** * @test */ - public function it_does_not_allow_that_two_pattern_matches_with_same_command_name() + public function it_does_not_allow_that_two_pattern_matches_with_same_command_name(): void { - $regexRouter = new RegexRouter(); + $this->expectException(RuntimeException::class); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to("DoSomethingHandler"); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*/')->to("DoSomethingHandler2"); + $regexRouter = new RegexRouter(); - $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/')->to('DoSomethingHandler'); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*/')->to('DoSomethingHandler2'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $regexRouter->onRouteMessage($actionEvent); } @@ -83,35 +94,43 @@ public function it_does_not_allow_that_two_pattern_matches_with_same_command_nam /** * @test */ - public function it_matches_pattern_with_event_name_and_routes_to_multiple_listeners() + public function it_matches_pattern_with_event_name_and_routes_to_multiple_listeners(): void { $regexRouter = new RegexRouter(); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to("SomethingDoneListener1"); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to("SomethingDoneListener2"); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to('SomethingDoneListener1'); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to('SomethingDoneListener2'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', + ] + ); $regexRouter->onRouteMessage($actionEvent); - $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); + $this->assertEquals(['SomethingDoneListener1', 'SomethingDoneListener2'], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); } /** * @test */ - public function it_returns_early_when_message_name_is_empty_on_multiple_listeners() + public function it_returns_early_when_message_name_is_empty_on_multiple_listeners(): void { $regexRouter = new RegexRouter(); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to("SomethingDoneListener1"); - $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to("SomethingDoneListener2"); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to('SomethingDoneListener1'); + $regexRouter->route('/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/')->to('SomethingDoneListener2'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - '' => 'ProophTest\ServiceBus\Mock\SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + '' => 'ProophTest\ServiceBus\Mock\SomethingDone', + ] + ); $regexRouter->onRouteMessage($actionEvent); @@ -120,10 +139,11 @@ public function it_returns_early_when_message_name_is_empty_on_multiple_listener /** * @test - * @expectedException \Prooph\ServiceBus\Exception\RuntimeException */ - public function it_fails_on_routing_a_second_pattern_before_first_definition_is_finished() + public function it_fails_on_routing_a_second_pattern_before_first_definition_is_finished(): void { + $this->expectException(RuntimeException::class); + $router = new RegexRouter(); $router->route('ProophTest\ServiceBus\Mock\DoSomething'); @@ -133,10 +153,11 @@ public function it_fails_on_routing_a_second_pattern_before_first_definition_is_ /** * @test - * @expectedException \Prooph\ServiceBus\Exception\RuntimeException */ - public function it_fails_on_setting_a_handler_before_a_pattern_is_set() + public function it_fails_on_setting_a_handler_before_a_pattern_is_set(): void { + $this->expectException(RuntimeException::class); + $router = new RegexRouter(); $router->to('DoSomethingHandler'); @@ -144,10 +165,11 @@ public function it_fails_on_setting_a_handler_before_a_pattern_is_set() /** * @test - * @expectedException \Prooph\ServiceBus\Exception\InvalidArgumentException */ - public function it_fails_when_routing_to_invalid_handler() + public function it_fails_when_routing_to_invalid_handler(): void { + $this->expectException(InvalidArgumentException::class); + $router = new RegexRouter(); $router->to(null); @@ -156,56 +178,70 @@ public function it_fails_when_routing_to_invalid_handler() /** * @test */ - public function it_takes_a_routing_definition_on_instantiation() + public function it_takes_a_routing_definition_on_instantiation(): void { $router = new RegexRouter([ '/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/' => 'DoSomethingHandler', - '/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/' => ["SomethingDoneListener1", "SomethingDoneListener2"] - + '/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $this->assertEquals('DoSomethingHandler', $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', + ] + ); $router->onRouteMessage($actionEvent); - $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); + $this->assertEquals(['SomethingDoneListener1', 'SomethingDoneListener2'], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); } /** * @test */ - public function it_still_works_if_deprecated_method_on_route_is_used() + public function it_still_works_if_deprecated_method_on_route_is_used(): void { $router = new RegexRouter([ '/^'.preg_quote('ProophTest\ServiceBus\Mock\Do').'.*/' => 'DoSomethingHandler', - '/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/' => ["SomethingDoneListener1", "SomethingDoneListener2"] - + '/^'.preg_quote('ProophTest\ServiceBus\Mock\\').'.*Done$/' => ['SomethingDoneListener1', 'SomethingDoneListener2'], ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); - $router->onRoute($actionEvent); + $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $this->assertEquals('DoSomethingHandler', $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\SomethingDone', + ] + ); - $router->onRoute($actionEvent); + $router->onRouteMessage($actionEvent); - $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); + $this->assertEquals(['SomethingDoneListener1', 'SomethingDoneListener2'], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); } } diff --git a/tests/Plugin/Router/ServiceLocatorEventRouterTest.php b/tests/Plugin/Router/ServiceLocatorEventRouterTest.php new file mode 100644 index 0000000..fb29d12 --- /dev/null +++ b/tests/Plugin/Router/ServiceLocatorEventRouterTest.php @@ -0,0 +1,51 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ProophTest\ServiceBus\Plugin\Router; + +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\DefaultActionEvent; +use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Router\ServiceLocatorEventRouter; +use ProophTest\ServiceBus\Mock\MessageHandler; +use Psr\Container\ContainerInterface; + +class ServiceLocatorEventRouterTest extends TestCase +{ + /** + * @test + */ + public function it_routes() + { + $container = $this->prophesize(ContainerInterface::class); + $container->has('event')->willReturn(true)->shouldBeCalled(); + $container->get('event')->willReturn(new MessageHandler())->shouldBeCalled(); + + $eventBus = new EventBus(); + + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new EventBus(), + [ + EventBus::EVENT_PARAM_MESSAGE_NAME => 'event', + ] + ); + + $router = new ServiceLocatorEventRouter($container->reveal()); + $router->attachToMessageBus($eventBus); + + $router->onRouteMessage($actionEvent); + + $this->assertInstanceOf(MessageHandler::class, $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); + } +} diff --git a/tests/Plugin/Router/SingleHandlerRouterTest.php b/tests/Plugin/Router/SingleHandlerRouterTest.php index e2fe161..fc41b76 100644 --- a/tests/Plugin/Router/SingleHandlerRouterTest.php +++ b/tests/Plugin/Router/SingleHandlerRouterTest.php @@ -1,53 +1,56 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin\Router; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\Exception\InvalidArgumentException; +use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\Router\CommandRouter; -use ProophTest\ServiceBus\TestCase; -/** - * Class SingleHandlerRouterTest - * - * @package ProophTest\ServiceBus\Plugin\Router - * @author Alexander Miertsch - */ class SingleHandlerRouterTest extends TestCase { /** * @test */ - public function it_can_handle_routing_definition_by_chaining_route_to() + public function it_can_handle_routing_definition_by_chaining_route_to(): void { $router = new CommandRouter(); - $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to("DoSomethingHandler"); + $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to('DoSomethingHandler'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $this->assertEquals('DoSomethingHandler', $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } /** * @test - * @expectedException \Prooph\ServiceBus\Exception\InvalidArgumentException */ - public function it_fails_when_routing_to_invalid_handler() + public function it_fails_when_routing_to_invalid_handler(): void { + $this->expectException(InvalidArgumentException::class); + $router = new CommandRouter(); $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to(null); @@ -56,15 +59,19 @@ public function it_fails_when_routing_to_invalid_handler() /** * @test */ - public function it_returns_early_when_message_name_is_empty() + public function it_returns_early_when_message_name_is_empty(): void { $router = new CommandRouter(); - $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to("DoSomethingHandler"); + $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to('DoSomethingHandler'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'unknown', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'unknown', + ] + ); $router->onRouteMessage($actionEvent); @@ -74,15 +81,19 @@ public function it_returns_early_when_message_name_is_empty() /** * @test */ - public function it_returns_early_when_message_name_is_not_in_event_map() + public function it_returns_early_when_message_name_is_not_in_event_map(): void { $router = new CommandRouter(); - $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to("DoSomethingHandler"); + $router->route('ProophTest\ServiceBus\Mock\DoSomething')->to('DoSomethingHandler'); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - '' => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + '' => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $router->onRouteMessage($actionEvent); @@ -92,25 +103,25 @@ public function it_returns_early_when_message_name_is_not_in_event_map() /** * @test */ - public function it_fails_on_routing_a_second_command_before_first_definition_is_finished() + public function it_fails_on_routing_a_second_command_before_first_definition_is_finished(): void { + $this->expectException(RuntimeException::class); + $router = new CommandRouter(); $router->route('ProophTest\ServiceBus\Mock\DoSomething'); - $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); - $router->route('AnotherCommand'); } /** * @test */ - public function it_fails_on_setting_a_handler_before_a_command_is_set() + public function it_fails_on_setting_a_handler_before_a_command_is_set(): void { - $router = new CommandRouter(); + $this->expectException(RuntimeException::class); - $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); + $router = new CommandRouter(); $router->to('DoSomethingHandler'); } @@ -118,18 +129,22 @@ public function it_fails_on_setting_a_handler_before_a_command_is_set() /** * @test */ - public function it_takes_a_routing_definition_on_instantiation() + public function it_takes_a_routing_definition_on_instantiation(): void { $router = new CommandRouter([ - 'ProophTest\ServiceBus\Mock\DoSomething' => 'DoSomethingHandler' + 'ProophTest\ServiceBus\Mock\DoSomething' => 'DoSomethingHandler', ]); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', - ]); + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + new CommandBus(), + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'ProophTest\ServiceBus\Mock\DoSomething', + ] + ); $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $this->assertEquals('DoSomethingHandler', $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } } diff --git a/tests/Plugin/Router/SingleHandlerServiceLocatorRouterTest.php b/tests/Plugin/Router/SingleHandlerServiceLocatorRouterTest.php new file mode 100644 index 0000000..1ce4515 --- /dev/null +++ b/tests/Plugin/Router/SingleHandlerServiceLocatorRouterTest.php @@ -0,0 +1,51 @@ + + * (c) 2015-2017 Sascha-Oliver Prolic + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ProophTest\ServiceBus\Plugin\Router; + +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\DefaultActionEvent; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Router\SingleHandlerServiceLocatorRouter; +use ProophTest\ServiceBus\Mock\MessageHandler; +use Psr\Container\ContainerInterface; + +class SingleHandlerServiceLocatorRouterTest extends TestCase +{ + /** + * @test + */ + public function it_routes() + { + $container = $this->prophesize(ContainerInterface::class); + $container->has('message')->willReturn(true)->shouldBeCalled(); + $container->get('message')->willReturn(new MessageHandler())->shouldBeCalled(); + + $commandBus = new CommandBus(); + + $actionEvent = new DefaultActionEvent( + MessageBus::EVENT_DISPATCH, + $commandBus, + [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'message', + ] + ); + + $router = new SingleHandlerServiceLocatorRouter($container->reveal()); + $router->attachToMessageBus($commandBus); + + $router->onRouteMessage($actionEvent); + + $this->assertInstanceOf(MessageHandler::class, $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + } +} diff --git a/tests/Plugin/ServiceLocatorPluginTest.php b/tests/Plugin/ServiceLocatorPluginTest.php index 1164862..ac30c75 100644 --- a/tests/Plugin/ServiceLocatorPluginTest.php +++ b/tests/Plugin/ServiceLocatorPluginTest.php @@ -1,50 +1,52 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus\Plugin; -use Interop\Container\ContainerInterface; -use Prooph\Common\Event\DefaultActionEvent; +use PHPUnit\Framework\TestCase; +use Prooph\Common\Event\ActionEvent; use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\MessageBus; use Prooph\ServiceBus\Plugin\ServiceLocatorPlugin; use ProophTest\ServiceBus\Mock\MessageHandler; -use ProophTest\ServiceBus\TestCase; +use Psr\Container\ContainerInterface; -/** - * Class ServiceLocatorPluginTest - * @package ProophTest\ServiceBus\Plugin - */ -final class ServiceLocatorPluginTest extends TestCase +class ServiceLocatorPluginTest extends TestCase { /** * @test */ - public function it_locates_a_service_using_the_message_handler_param_of_the_action_event() + public function it_locates_a_service_using_the_message_handler_param_of_the_action_event(): void { $handler = new MessageHandler(); $container = $this->prophesize(ContainerInterface::class); - $container->has("custom-handler")->willReturn(true); + $container->has('custom-handler')->willReturn(true)->shouldBeCalled(); - $container->get("custom-handler")->willReturn($handler); + $container->get('custom-handler')->willReturn($handler)->shouldBeCalled(); - $locatorPlugin = new ServiceLocatorPlugin($container->reveal()); + $commandBus = new CommandBus(); - $actionEvent = new DefaultActionEvent(MessageBus::EVENT_LOCATE_HANDLER, new CommandBus(), [ - MessageBus::EVENT_PARAM_MESSAGE_HANDLER => "custom-handler" - ]); + $locatorPlugin = new ServiceLocatorPlugin($container->reveal()); + $locatorPlugin->attachToMessageBus($commandBus); - $locatorPlugin->onLocateMessageHandler($actionEvent); + $commandBus->attach( + CommandBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent): void { + $actionEvent->setParam(CommandBus::EVENT_PARAM_MESSAGE_HANDLER, 'custom-handler'); + }, + CommandBus::PRIORITY_INITIALIZE + ); - $this->assertSame($handler, $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + $commandBus->dispatch('foo'); } } diff --git a/tests/QueryBusTest.php b/tests/QueryBusTest.php index 53c4585..d518e1d 100644 --- a/tests/QueryBusTest.php +++ b/tests/QueryBusTest.php @@ -1,17 +1,19 @@ - * (c) 2015-2016 Sascha-Oliver Prolic + * (c) 2014-2017 prooph software GmbH + * (c) 2015-2017 Sascha-Oliver Prolic * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace ProophTest\ServiceBus; +use PHPUnit\Framework\TestCase; use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\Exception\MessageDispatchException; use Prooph\ServiceBus\Exception\RuntimeException; use Prooph\ServiceBus\MessageBus; @@ -24,11 +26,7 @@ use React\Promise\Deferred; use React\Promise\Promise; -/** - * Class QueryBusTest - * @package ProophTest\ServiceBus - */ -final class QueryBusTest extends TestCase +class QueryBusTest extends TestCase { /** * @var QueryBus @@ -39,25 +37,33 @@ protected function setUp() { $this->queryBus = new QueryBus(); } + /** * @test */ - public function it_dispatches_a_message_using_the_default_process() + public function it_dispatches_a_message_using_the_default_process(): void { $fetchSomething = new FetchSomething(['filter' => 'todo']); $receivedMessage = null; $dispatchEvent = null; - $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, function (FetchSomething $fetchSomething, Deferred $deferred) use (&$receivedMessage) { - $deferred->resolve($fetchSomething); - }); - $dispatchEvent = $actionEvent; - }); + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$receivedMessage, &$dispatchEvent): void { + $actionEvent->setParam( + MessageBus::EVENT_PARAM_MESSAGE_HANDLER, + function (FetchSomething $fetchSomething, Deferred $deferred) use (&$receivedMessage): void { + $deferred->resolve($fetchSomething); + } + ); + $dispatchEvent = $actionEvent; + }, + MessageBus::PRIORITY_ROUTE + ); $promise = $this->queryBus->dispatch($fetchSomething); - $promise->then(function ($result) use (&$receivedMessage) { + $promise->then(function ($result) use (&$receivedMessage): void { $receivedMessage = $result; }); @@ -68,90 +74,86 @@ public function it_dispatches_a_message_using_the_default_process() /** * @test */ - public function it_triggers_all_defined_action_events() + public function it_triggers_all_defined_action_events(): void { $initializeIsTriggered = false; $detectMessageNameIsTriggered = false; $routeIsTriggered = false; $locateHandlerIsTriggered = false; $invokeFinderIsTriggered = false; - $handleErrorIsTriggered = false; $finalizeIsTriggered = false; //Should always be triggered - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, - function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered): void { $initializeIsTriggered = true; - } + }, + MessageBus::PRIORITY_INITIALIZE ); //Should be triggered because we dispatch a message that does not //implement Prooph\Common\Messaging\HasMessageName - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_DETECT_MESSAGE_NAME, - function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered): void { $detectMessageNameIsTriggered = true; - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); - } + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, 'custom-message'); + }, + MessageBus::PRIORITY_DETECT_MESSAGE_NAME ); //Should be triggered because we did not provide a message-handler yet - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_ROUTE, - function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$routeIsTriggered): void { $routeIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === 'custom-message') { //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event - $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, "error-producer"); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, 'error-producer'); } - } + }, + MessageBus::PRIORITY_ROUTE ); //Should be triggered because we provided the message-handler as string (service id) - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_LOCATE_HANDLER, - function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered): void { $locateHandlerIsTriggered = true; - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === 'error-producer') { $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); } - } + }, + MessageBus::PRIORITY_LOCATE_HANDLER ); //Should be triggered because the message-handler is not callable - $this->queryBus->getActionEventEmitter()->attachListener( - QueryBus::EVENT_INVOKE_FINDER, - function (ActionEvent $actionEvent) use (&$invokeFinderIsTriggered) { + $this->queryBus->attach( + QueryBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) use (&$invokeFinderIsTriggered): void { $invokeFinderIsTriggered = true; $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); if ($handler instanceof ErrorProducer) { $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); } - } - ); - - //Should be triggered because the message-handler threw an exception - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_HANDLE_ERROR, - function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { - $handleErrorIsTriggered = true; - - if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { - $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); - } - } + }, + QueryBus::PRIORITY_INVOKE_HANDLER ); //Should always be triggered - $this->queryBus->getActionEventEmitter()->attachListener( + $this->queryBus->attach( MessageBus::EVENT_FINALIZE, - function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered): void { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Throwable) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); + } $finalizeIsTriggered = true; - } + }, + 1000 // high priority ); - $customMessage = new CustomMessage("I have no further meaning"); + $customMessage = new CustomMessage('I have no further meaning'); $this->queryBus->dispatch($customMessage); @@ -160,26 +162,29 @@ function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { $this->assertTrue($routeIsTriggered); $this->assertTrue($locateHandlerIsTriggered); $this->assertTrue($invokeFinderIsTriggered); - $this->assertTrue($handleErrorIsTriggered); $this->assertTrue($finalizeIsTriggered); } /** * @test */ - public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name(): void { $handler = new Finder(); - $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { - if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { - $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); - } - }); + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e) use ($handler): void { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + } + }, + MessageBus::PRIORITY_ROUTE + ); - $this->queryBus->utilize(new FinderInvokeStrategy()); + (new FinderInvokeStrategy())->attachToMessageBus($this->queryBus); - $customMessage = new CustomMessage("foo"); + $customMessage = new CustomMessage('foo'); $promise = $this->queryBus->dispatch($customMessage); @@ -191,88 +196,101 @@ public function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided /** * @test */ - public function it_rejects_the_deferred_with_a_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + public function it_rejects_the_deferred_with_a_service_bus_exception_if_exception_is_not_handled_by_a_plugin(): void { $exception = null; - $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { - throw new \Exception("ka boom"); - }); + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function () { + throw new \Exception('ka boom'); + }, + MessageBus::PRIORITY_INITIALIZE + ); - $promise = $this->queryBus->dispatch("throw it"); + $promise = $this->queryBus->dispatch('throw it'); - $promise->otherwise(function ($ex) use (&$exception) { + $promise->otherwise(function ($ex) use (&$exception): void { $exception = $ex; }); $this->assertInstanceOf(MessageDispatchException::class, $exception); - $this->assertInstanceOf(DefaultActionEvent::class, $exception->getFailedDispatchEvent()); } /** * @test */ - public function it_throws_exception_if_event_has_no_handler_after_it_has_been_set_and_event_was_triggered() + public function it_throws_exception_if_event_has_no_handler_after_it_has_been_set_and_event_was_triggered(): void { $exception = null; - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, function (ActionEvent $e) { + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { $e->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER, null); - } + }, + MessageBus::PRIORITY_INITIALIZE ); - $promise = $this->queryBus->dispatch("throw it"); + $promise = $this->queryBus->dispatch('throw it'); - $promise->otherwise(function ($ex) use (&$exception) { + $promise->otherwise(function ($ex) use (&$exception): void { $exception = $ex; }); $this->assertInstanceOf(RuntimeException::class, $exception); - $this->assertEquals('Message dispatch failed during route phase. Error: QueryBus was not able to identify a Finder for query throw it', $exception->getMessage()); + $this->assertEquals('Message dispatch failed. See previous exception for details.', $exception->getMessage()); } /** * @test */ - public function it_throws_exception_if_event_has_stopped_propagation() + public function it_throws_exception_if_event_has_stopped_propagation(): void { $exception = null; - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, function (ActionEvent $e) { - $e->stopPropagation(true); - } + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { + throw new \RuntimeException('throw it!'); + }, + MessageBus::PRIORITY_INITIALIZE ); - $promise = $this->queryBus->dispatch("throw it"); + $promise = $this->queryBus->dispatch('throw it'); - $promise->otherwise(function ($ex) use (&$exception) { + $promise->otherwise(function ($ex) use (&$exception): void { $exception = $ex; }); - $this->assertInstanceOf(RuntimeException::class, $exception); - $this->assertEquals('Message dispatch failed during initialize phase. Error: Dispatch has stopped unexpectedly.', $exception->getMessage()); + $this->assertInstanceOf(MessageDispatchException::class, $exception); + $this->assertEquals('Message dispatch failed. See previous exception for details.', $exception->getMessage()); + $this->assertInstanceOf(\RuntimeException::class, $exception->getPrevious()); + $this->assertEquals('throw it!', $exception->getPrevious()->getMessage()); } /** * @test */ - public function it_can_deactive_an_action_event_listener_aggregate() + public function it_can_deactive_an_action_event_listener_aggregate(): void { $handler = new Finder(); - $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { - if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { - $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); - } - }); + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e) use ($handler): void { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + } + }, + MessageBus::PRIORITY_ROUTE + ); $plugin = new FinderInvokeStrategy(); - $this->queryBus->utilize($plugin); - $this->queryBus->deactivate($plugin); + $plugin->attachToMessageBus($this->queryBus); + $plugin->detachFromMessageBus($this->queryBus); - $customMessage = new CustomMessage("foo"); + $customMessage = new CustomMessage('foo'); $promise = $this->queryBus->dispatch($customMessage); @@ -283,19 +301,55 @@ public function it_can_deactive_an_action_event_listener_aggregate() /** * @test - * @expectedException Prooph\ServiceBus\Exception\RuntimeException */ - public function it_throws_exception_if_message_was_not_handled() + public function it_throws_exception_if_message_was_not_handled(): void { - $this->queryBus->getActionEventEmitter()->attachListener( - MessageBus::EVENT_INITIALIZE, - function (ActionEvent $e) { + $this->expectException(RuntimeException::class); + + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $e): void { $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new \stdClass()); - } + }, + MessageBus::PRIORITY_INITIALIZE ); $promise = $this->queryBus->dispatch('throw it'); $promise->done(); } + + /** + * @test + */ + public function it_could_reset_exception_before_promise_becomes_rejected(): void + { + $exceptionParamWasSet = false; + + $this->queryBus->attach( + MessageBus::EVENT_DISPATCH, + function (ActionEvent $actionEvent) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, function (): void { + throw new \Exception('Unset me!'); + }); + }, + MessageBus::PRIORITY_INITIALIZE + ); + + $this->queryBus->attach( + MessageBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$exceptionParamWasSet): void { + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Throwable) { + $exceptionParamWasSet = true; + $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); + } + }, + MessageBus::PRIORITY_PROMISE_REJECT + 1 + ); + + $promise = $this->queryBus->dispatch('throw an exception!'); + $promise->done(); + + $this->assertTrue($exceptionParamWasSet); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index 5dc8931..0000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,21 +0,0 @@ - - * (c) 2015-2016 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ProophTest\ServiceBus; - -/** - * Class TestCase - * - * @package ProophTest\ServiceBus - * @author Alexander Miertsch - */ -class TestCase extends \PHPUnit_Framework_TestCase -{ -}