From d7999e62759161b3eeed4066879f5240104ca368 Mon Sep 17 00:00:00 2001 From: butschster Date: Mon, 25 Dec 2023 14:19:04 +0400 Subject: [PATCH] Adds an ability to register interceptors via bootloader --- src/Bootloader/TemporalBridgeBootloader.php | 35 +++++++++- src/Dispatcher.php | 5 +- .../TemporalBridgeBootloaderTest.php | 70 ++++++++++++++++++- tests/src/DispatcherTest.php | 3 - 4 files changed, 104 insertions(+), 9 deletions(-) diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index 7399464..95f60ce 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -24,7 +24,6 @@ use Spiral\TemporalBridge\WorkersRegistry; use Spiral\TemporalBridge\WorkersRegistryInterface; use Spiral\Tokenizer\ClassesInterface; -use Temporal\Client\ClientOptions; use Temporal\Client\GRPC\ServiceClient; use Temporal\Client\WorkflowClient; use Temporal\Client\WorkflowClientInterface; @@ -41,6 +40,9 @@ use Temporal\Client\ScheduleClientInterface; use Temporal\Client\GRPC\ServiceClientInterface; +/** + * @psalm-import-type TInterceptor from TemporalConfig + */ class TemporalBridgeBootloader extends Bootloader { public function defineDependencies(): array @@ -69,17 +71,18 @@ public function defineSingletons(): array public function __construct( private readonly ConfiguratorInterface $config, + private readonly FactoryInterface $factory, ) { } public function init( AbstractKernel $kernel, EnvironmentInterface $env, - FactoryInterface $factory, + Dispatcher $dispatcher, ): void { $this->initConfig($env); - $kernel->addDispatcher($factory->make(Dispatcher::class)); + $kernel->addDispatcher($dispatcher); } public function addWorkerOptions(string $worker, WorkerOptions $options): void @@ -87,6 +90,32 @@ public function addWorkerOptions(string $worker, WorkerOptions $options): void $this->config->modify(TemporalConfig::CONFIG, new Append('workers', $worker, $options)); } + /** + * Register a new Temporal interceptor. + * + * @param TInterceptor $interceptor + */ + public function addInterceptor(string|Interceptor|Autowire $interceptor): void + { + if (\is_string($interceptor)) { + $interceptor = $this->factory->make($interceptor); + } elseif ($interceptor instanceof Autowire) { + $interceptor = $interceptor->resolve($this->factory); + } + + if (!$interceptor instanceof Interceptor) { + throw new \InvalidArgumentException( + \sprintf( + 'Interceptor must be an instance of `%s`, `%s` given.', + Interceptor::class, + \get_class($interceptor), + ), + ); + } + + $this->config->modify(TemporalConfig::CONFIG, new Append('interceptors', null, $interceptor)); + } + protected function initConfig(EnvironmentInterface $env): void { $this->config->setDefaults( diff --git a/src/Dispatcher.php b/src/Dispatcher.php index b6db7fa..a0e0b3a 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -20,7 +20,6 @@ final class Dispatcher implements DispatcherInterface public function __construct( private readonly RoadRunnerMode $mode, private readonly ReaderInterface $reader, - private readonly TemporalConfig $config, private readonly Container $container, ) { } @@ -32,6 +31,8 @@ public function canServe(): bool public function serve(): void { + $config = $this->container->get(TemporalConfig::class); + // finds all available workflows, activity types and commands in a given directory /** * @var array|class-string, ReflectionClass> $declarations @@ -45,7 +46,7 @@ public function serve(): void $hasDeclarations = false; foreach ($declarations as $type => $declaration) { // Worker that listens on a task queue and hosts both workflow and activity implementations. - $queueName = $this->resolveQueueName($declaration) ?? $this->config->getDefaultWorker(); + $queueName = $this->resolveQueueName($declaration) ?? $config->getDefaultWorker(); $worker = $registry->get($queueName); diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index 9a15c46..5a4b7e7 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -4,6 +4,9 @@ namespace Spiral\TemporalBridge\Tests\Bootloader; +use Mockery as m; +use Spiral\Core\Container\Autowire; +use Spiral\Core\FactoryInterface; use Spiral\TemporalBridge\Bootloader\TemporalBridgeBootloader; use Spiral\TemporalBridge\Config\TemporalConfig; use Spiral\Config\ConfigManager; @@ -25,6 +28,7 @@ use Temporal\DataConverter\DataConverterInterface; use Temporal\Interceptor\SimplePipelineProvider; use Temporal\Interceptor\PipelineProvider; +use Temporal\Internal\Interceptor\Interceptor; use Temporal\Worker\WorkerFactoryInterface as TemporalWorkerFactoryInterface; use Temporal\Worker\WorkerOptions; use Temporal\WorkerFactory as TemporalWorkerFactory; @@ -108,7 +112,7 @@ public function testAddWorkerOptions(): void $configs = new ConfigManager($this->createMock(LoaderInterface::class)); $configs->setDefaults(TemporalConfig::CONFIG, ['workers' => []]); - $bootloader = new TemporalBridgeBootloader($configs); + $bootloader = new TemporalBridgeBootloader($configs, $this->getContainer()); $bootloader->addWorkerOptions('first', $first = WorkerOptions::new()); $bootloader->addWorkerOptions('second', $second = WorkerOptions::new()); @@ -117,4 +121,68 @@ public function testAddWorkerOptions(): void $configs->getConfig(TemporalConfig::CONFIG)['workers'], ); } + + public function testAddInterceptor(): void + { + $configs = new ConfigManager($this->createMock(LoaderInterface::class)); + $configs->setDefaults(TemporalConfig::CONFIG, ['interceptors' => []]); + + $bootloader = new TemporalBridgeBootloader($configs, $this->getContainer()); + + $bootloader->addInterceptor($iterceptor = m::mock(Interceptor::class)); + + $this->assertSame( + [$iterceptor], + $configs->getConfig(TemporalConfig::CONFIG)['interceptors'], + ); + } + + public function testStringableInterceptor(): void + { + $configs = new ConfigManager($this->createMock(LoaderInterface::class)); + $configs->setDefaults(TemporalConfig::CONFIG, ['interceptors' => []]); + + $bootloader = new TemporalBridgeBootloader($configs, $factory = m::mock(FactoryInterface::class)); + + $factory->shouldReceive('make')->with('foo')->andReturn($iterceptor = m::mock(Interceptor::class)); + + $bootloader->addInterceptor('foo'); + + $this->assertSame( + [$iterceptor], + $configs->getConfig(TemporalConfig::CONFIG)['interceptors'], + ); + } + + public function testAutowireInterceptor(): void + { + $configs = new ConfigManager($this->createMock(LoaderInterface::class)); + $configs->setDefaults(TemporalConfig::CONFIG, ['interceptors' => []]); + + $bootloader = new TemporalBridgeBootloader($configs, $factory = m::mock(FactoryInterface::class)); + + $factory->shouldReceive('make')->with('foo', ['bar' => 'baz'])->andReturn($iterceptor = m::mock(Interceptor::class)); + + $bootloader->addInterceptor(new Autowire('foo', ['bar' => 'baz'])); + + $this->assertSame( + [$iterceptor], + $configs->getConfig(TemporalConfig::CONFIG)['interceptors'], + ); + } + + public function testInvalidInterceptor(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Interceptor must be an instance of `Temporal\Internal\Interceptor\Interceptor`, `stdClass` given.'); + + $configs = new ConfigManager($this->createMock(LoaderInterface::class)); + $configs->setDefaults(TemporalConfig::CONFIG, ['interceptors' => []]); + + $bootloader = new TemporalBridgeBootloader($configs, $factory = m::mock(FactoryInterface::class)); + + $factory->shouldReceive('make')->with('foo')->andReturn(new \StdClass()); + + $bootloader->addInterceptor('foo'); + } } diff --git a/tests/src/DispatcherTest.php b/tests/src/DispatcherTest.php index 75f33b6..155cebf 100644 --- a/tests/src/DispatcherTest.php +++ b/tests/src/DispatcherTest.php @@ -28,7 +28,6 @@ protected function setUp(): void $this->dispatcher = new Dispatcher( RoadRunnerMode::Temporal, new AttributeReader(), - new TemporalConfig(['defaultWorker' => 'foo']), $this->getContainer(), ); @@ -72,7 +71,6 @@ public function testServeWithoutDeclarations(): void $dispatcher = new Dispatcher( RoadRunnerMode::Temporal, new AttributeReader(), - new TemporalConfig(), $this->getContainer(), ); @@ -97,7 +95,6 @@ public function testServeWithDeclarations(): void $dispatcher = new Dispatcher( RoadRunnerMode::Temporal, new AttributeReader(), - new TemporalConfig(), $this->getContainer(), );