Skip to content

Commit ba149d6

Browse files
committed
Client factory
1 parent 31cd489 commit ba149d6

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

src/Factory.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Foundation;
4+
5+
use ApiClients\Foundation\Hydrator\Factory as HydratorFactory;
6+
use ApiClients\Foundation\Hydrator\Hydrator;
7+
use ApiClients\Foundation\Hydrator\Options as HydratorOptions;
8+
use ApiClients\Foundation\Transport\Client as TransportClient;
9+
use ApiClients\Foundation\Transport\Factory as TransportFactory;
10+
use ApiClients\Foundation\Transport\Options as TransportOptions;
11+
use Interop\Container\ContainerInterface;
12+
use League\Container\Container;
13+
use League\Container\ReflectionContainer;
14+
use League\Event\Emitter;
15+
use League\Event\EmitterInterface;
16+
use League\Tactician\CommandBus;
17+
use League\Tactician\Container\ContainerLocator;
18+
use League\Tactician\Handler\CommandHandlerMiddleware;
19+
use League\Tactician\Handler\CommandNameExtractor\ClassNameExtractor;
20+
use League\Tactician\Handler\MethodNameInflector\HandleInflector;
21+
use React\EventLoop\LoopInterface;
22+
23+
final class Factory
24+
{
25+
public static function create(
26+
LoopInterface $loop = null,
27+
ContainerInterface $wrappedContainer = null,
28+
array $options = []
29+
): Client {
30+
$container = self::createContainer($wrappedContainer);
31+
32+
$container->share(EmitterInterface::class, new Emitter());
33+
$container->share(TransportClient::class, self::createTransport($container, $loop, $options));
34+
$container->share(Hydrator::class, self::createHydrator($container, $options));
35+
$container->share(CommandBus::class, function () use ($container) {
36+
return self::createCommandBus($container);
37+
});
38+
39+
return new Client(
40+
$container
41+
);
42+
}
43+
44+
private static function createContainer(ContainerInterface $wrappedContainer = null): Container
45+
{
46+
$container = new Container();
47+
$container->delegate(new ReflectionContainer());
48+
49+
if ($wrappedContainer instanceof ContainerInterface) {
50+
$container->delegate($wrappedContainer);
51+
}
52+
53+
return $container;
54+
}
55+
56+
private static function createCommandBus(ContainerInterface $container): CommandBus
57+
{
58+
$commandToHandlerMap = self::mapCommandsToHandlers($container->get(EmitterInterface::class));
59+
60+
$containerLocator = new ContainerLocator(
61+
$container,
62+
$commandToHandlerMap
63+
);
64+
65+
$commandHandlerMiddleware = new CommandHandlerMiddleware(
66+
new ClassNameExtractor(),
67+
$containerLocator,
68+
new HandleInflector()
69+
);
70+
71+
return new CommandBus([
72+
$commandHandlerMiddleware,
73+
]);
74+
}
75+
76+
private static function mapCommandsToHandlers(Emitter $emitter): array
77+
{
78+
return $emitter->emit(CommandLocatorEvent::create())->getMap();
79+
}
80+
81+
private static function createTransport(
82+
ContainerInterface $container,
83+
LoopInterface $loop = null,
84+
array $options = []
85+
): TransportClient {
86+
$transport = TransportFactory::create($loop, $options);
87+
$container->share(LoopInterface::class, $transport->getLoop());
88+
return $transport;
89+
}
90+
91+
private static function createHydrator(ContainerInterface $container, array $options = [])
92+
{
93+
if (isset($options[TransportOptions::HYDRATOR]) && $options[TransportOptions::HYDRATOR] instanceof Hydrator) {
94+
return $options[TransportOptions::HYDRATOR];
95+
}
96+
97+
if (!isset($options[TransportOptions::HYDRATOR_OPTIONS])) {
98+
throw new \Exception('Missing Hydrator options');
99+
}
100+
101+
return HydratorFactory::create($options[TransportOptions::HYDRATOR_OPTIONS]);
102+
}
103+
}

tests/FactoryTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ApiClients\Tests\Foundation;
4+
5+
use ApiClients\Foundation\Client;
6+
use ApiClients\Foundation\CommandLocatorEvent;
7+
use ApiClients\Foundation\Factory;
8+
use ApiClients\Foundation\Transport\Options;
9+
use ApiClients\Tools\TestUtilities\TestCase;
10+
use League\Container\Container;
11+
use League\Event\CallbackListener;
12+
use League\Event\Emitter;
13+
use League\Event\EmitterInterface;
14+
use League\Tactician\Exception\MissingHandlerException;
15+
use React\EventLoop\LoopInterface;
16+
17+
final class FactoryTest extends TestCase
18+
{
19+
public function testCreate()
20+
{
21+
$loop = $this->prophesize(LoopInterface::class);
22+
23+
$client = Factory::create(
24+
$loop->reveal(),
25+
new Container(),
26+
[
27+
Options::HYDRATOR_OPTIONS => [],
28+
]
29+
);
30+
31+
$this->assertInstanceOf(Client::class, $client);
32+
33+
$container = $client->getContainer();
34+
35+
$called = false;
36+
$container->get(EmitterInterface::class)->addListener(
37+
CommandLocatorEvent::NAME,
38+
CallbackListener::fromCallable(
39+
function (CommandLocatorEvent $event) use (&$called) {
40+
//$this->assertSame([], $event->getMap());
41+
$called = true;
42+
}
43+
)
44+
);
45+
46+
$this->assertFalse($called);
47+
48+
try {
49+
$client->handle(new class() {});
50+
} catch (\Throwable $exception) {
51+
52+
}
53+
54+
$this->assertTrue($called);
55+
$this->assertTrue(isset($exception));
56+
$this->assertInstanceOf(MissingHandlerException::class, $exception);
57+
}
58+
}

0 commit comments

Comments
 (0)