Skip to content

Commit

Permalink
Merge 9a283d8 into 73cd015
Browse files Browse the repository at this point in the history
  • Loading branch information
gquemener committed May 24, 2018
2 parents 73cd015 + 9a283d8 commit e24fcb3
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 1 deletion.
4 changes: 3 additions & 1 deletion composer.json
Expand Up @@ -36,14 +36,16 @@
"psr/container": "^1.0",
"sandrokeil/interop-config": "^2.0.1",
"prooph/bookdown-template": "^0.2.3",
"malukenho/docheader": "^0.1.4"
"malukenho/docheader": "^0.1.4",
"psr/simple-cache": "^1.0"
},
"suggest": {
"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",
"psr/container": "^1.0 for usage of provided factories",
"psr/simple-cache": "^1.0 for usage of provided CachePlugin",
"sandrokeil/interop-config": "For usage of provided factories"
},
"conflict": {
Expand Down
20 changes: 20 additions & 0 deletions src/Plugin/CacheKeyGenerator.php
@@ -0,0 +1,20 @@
<?php
/**
* This file is part of the prooph/service-bus.
* (c) 2014-2018 prooph software GmbH <contact@prooph.de>
* (c) 2015-2018 Sascha-Oliver Prolic <saschaprolic@googlemail.com>
*
* 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\Messaging\Query;

interface CacheKeyGenerator
{
public function getCacheKey(Query $query): string;
}
30 changes: 30 additions & 0 deletions src/Plugin/CacheKeyGenerator/Standard.php
@@ -0,0 +1,30 @@
<?php
/**
* This file is part of the prooph/service-bus.
* (c) 2014-2018 prooph software GmbH <contact@prooph.de>
* (c) 2015-2018 Sascha-Oliver Prolic <saschaprolic@googlemail.com>
*
* 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\CacheKeyGenerator;

use Prooph\Common\Messaging\Query;
use Prooph\ServiceBus\Plugin\CacheKeyGenerator;

final class Standard implements CacheKeyGenerator
{
public function getCacheKey(Query $query): string
{
$keyParts = [$query->messageName()];
foreach ($query->payload() as $key => $value) {
$keyParts[] = $key;
$keyParts[] = $value;
}

return sha1(json_encode($keyParts));
}
}
68 changes: 68 additions & 0 deletions src/Plugin/CachePlugin.php
@@ -0,0 +1,68 @@
<?php
/**
* This file is part of the prooph/service-bus.
* (c) 2014-2018 prooph software GmbH <contact@prooph.de>
* (c) 2015-2018 Sascha-Oliver Prolic <saschaprolic@googlemail.com>
*
* 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\ServiceBus\MessageBus;
use Prooph\ServiceBus\QueryBus;
use Psr\SimpleCache\CacheInterface;

final class CachePlugin extends AbstractPlugin
{
private $cache;
private $keyGenerator;

public function __construct(
CacheInterface $cache,
CacheKeyGenerator $keyGenerator = null
) {
$this->cache = $cache;
$this->keyGenerator = $keyGenerator ?: new CacheKeyGenerator\Standard();
}

public function attachToMessageBus(MessageBus $messageBus): void
{
if (! $messageBus instanceof QueryBus) {
throw new \InvalidArgumentException(sprintf(
'The cache plugin can only be attached to an instance of "Prooph\ServiceBus\QueryBus", got "%s".',
get_class($messageBus)
));
}

$this->listenerHandlers[] = $messageBus->attach(
QueryBus::EVENT_DISPATCH,
function (ActionEvent $actionEvent): void {
if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLED, false)) {
return;
}

$query = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE);

$deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED);

$key = $this->keyGenerator->getCacheKey($query);
if (null !== $result = $this->cache->get($key)) {
$deferred->resolve($result);
$actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, null);

return;
}

$deferred->promise()->then(function ($data) use ($key) {
$this->cache->set($key, $data);
});
},
QueryBus::PRIORITY_INVOKE_HANDLER + 1
);
}
}
119 changes: 119 additions & 0 deletions tests/Plugin/CachePluginTest.php
@@ -0,0 +1,119 @@
<?php
/**
* This file is part of the prooph/service-bus.
* (c) 2014-2018 prooph software GmbH <contact@prooph.de>
* (c) 2015-2018 Sascha-Oliver Prolic <saschaprolic@googlemail.com>
*
* 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\Messaging\Query;
use Prooph\ServiceBus\CommandBus;
use Prooph\ServiceBus\EventBus;
use Prooph\ServiceBus\Plugin\CacheKeyGenerator;
use Prooph\ServiceBus\Plugin\CachePlugin;
use Prooph\ServiceBus\Plugin\Plugin;
use Prooph\ServiceBus\Plugin\Router\QueryRouter;
use Prooph\ServiceBus\QueryBus;
use Psr\SimpleCache\CacheInterface;
use React\Promise\Deferred;

class CachePluginTest extends TestCase
{
private $cache;
private $keyGen;
private $plugin;

public function setUp()
{
$this->cache = $this->prophesize(CacheInterface::class);
$this->keyGen = $this->prophesize(CacheKeyGenerator::class);
$this->plugin = new CachePlugin(
$this->cache->reveal(),
$this->keyGen->reveal()
);
}

/**
* @test
*/
public function it_is_a_message_bus_plugin()
{
$this->assertInstanceOf(Plugin::class, $this->plugin);
}

/**
* @test
*/
public function it_resolves_with_cached_value_on_hit()
{
$queryBus = new QueryBus();

$router = new QueryRouter();
$router->route('a-query')
->to(function ($query, Deferred $deferred): void {
});
$router->attachToMessageBus($queryBus);
$query = $this->prophesize(Query::class);
$this->plugin->attachToMessageBus($queryBus);

$query->messageName()->willReturn('a-query');
$this->keyGen->getCacheKey($query)->willReturn('cache-key');
$this->cache->get('cache-key')->willReturn('Hello World');

$promise = $queryBus->dispatch($query->reveal());
$value = null;
$promise->then(function ($result) use (&$value) {
$value = $result;
});
$this->assertEquals('Hello World', $value);
}

/**
* @test
*/
public function it_caches_resolved_value()
{
$queryBus = new QueryBus();

$router = new QueryRouter();
$router->route('a-query')
->to(function ($query, Deferred $deferred): void {
$deferred->resolve('Hello World');
});
$router->attachToMessageBus($queryBus);
$query = $this->prophesize(Query::class);
$this->plugin->attachToMessageBus($queryBus);

$query->messageName()->willReturn('a-query');
$this->keyGen->getCacheKey($query)->willReturn('cache-key');
$this->cache->get('cache-key')->willReturn(null);
$this->cache->set('cache-key', 'Hello World')->shouldBeCalled();

$queryBus->dispatch($query->reveal());
}

/**
* @test
*/
public function it_does_not_attach_to_command_bus()
{
$this->expectException(\InvalidArgumentException::class);
$this->plugin->attachToMessageBus(new CommandBus());
}

/**
* @test
*/
public function it_does_not_attach_to_event_bus()
{
$this->expectException(\InvalidArgumentException::class);
$this->plugin->attachToMessageBus(new EventBus());
}
}

0 comments on commit e24fcb3

Please sign in to comment.