From 2d262aded032eabe30c37b2a34c6763732770649 Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 10:30:39 +0400 Subject: [PATCH 01/19] Enhanced Temporal connection configuration for flexibility and SSL support This commit significantly overhauls the Temporal connection configuration system. The previous configuration was limited to specifying a single Temporal address with no support for SSL connections. With the new setup, developers can now define multiple connection options, including SSL-enabled connections. Changes include: - Introduction of a 'connections' array to specify multiple connection types (e.g., `default`, `SSL`). - Addition of the `SslConnection` class to handle SSL connection parameters such as certificates and keys. - Preservation of backward compatibility by supporting the previous `address` configuration under the `default` connection type. --- src/Bootloader/TemporalBridgeBootloader.php | 22 ++++++- src/Config/TemporalConfig.php | 45 +++++++++---- src/Connection/Connection.php | 23 +++++++ src/Connection/DsnConnection.php | 15 +++++ src/Connection/SslConnection.php | 25 +++++++ tests/app/config/temporal.php | 22 +++++++ tests/app/src/config/temporal.php | 7 -- .../TemporalBridgeBootloaderTest.php | 45 ++++++++++++- tests/src/Config/TemporalConfigTest.php | 65 +++++++++++-------- 9 files changed, 217 insertions(+), 52 deletions(-) create mode 100644 src/Connection/Connection.php create mode 100644 src/Connection/DsnConnection.php create mode 100644 src/Connection/SslConnection.php create mode 100644 tests/app/config/temporal.php delete mode 100644 tests/app/src/config/temporal.php diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index deecddd..81495cb 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -16,6 +16,8 @@ use Spiral\RoadRunnerBridge\Bootloader\RoadRunnerBootloader; use Spiral\TemporalBridge\Commands; use Spiral\TemporalBridge\Config\TemporalConfig; +use Spiral\TemporalBridge\Connection\SslConnection; +use Spiral\TemporalBridge\Connection\DsnConnection; use Spiral\TemporalBridge\DeclarationLocator; use Spiral\TemporalBridge\DeclarationLocatorInterface; use Spiral\TemporalBridge\Dispatcher; @@ -121,8 +123,10 @@ protected function initConfig(EnvironmentInterface $env): void $this->config->setDefaults( TemporalConfig::CONFIG, [ - 'address' => $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), - 'namespace' => 'App\\Endpoint\\Temporal\\Workflow', + 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), + 'connections' => [ + 'default' => new DsnConnection(address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233')), + ], 'defaultWorker' => (string)$env->get( 'TEMPORAL_TASK_QUEUE', TemporalWorkerFactoryInterface::DEFAULT_TASK_QUEUE, @@ -185,7 +189,19 @@ protected function initPipelineProvider(TemporalConfig $config, FactoryInterface protected function initServiceClient(TemporalConfig $config): ServiceClientInterface { - return ServiceClient::create($config->getAddress()); + $connection = $config->getConnection($config->getDefaultConnection()); + + if ($connection instanceof SslConnection) { + return ServiceClient::createSSL( + address: $connection->getAddress(), + crt: $connection->crt, + clientKey: $connection->clientKey, + clientPem: $connection->clientPem, + overrideServerName: $connection->overrideServerName, + ); + } + + return ServiceClient::create(address: $connection->getAddress()); } protected function initScheduleClient( diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index 9853921..04b7d63 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -6,6 +6,9 @@ use Spiral\Core\Container\Autowire; use Spiral\Core\InjectableConfig; +use Spiral\TemporalBridge\Connection\Connection; +use Spiral\TemporalBridge\Connection\DsnConnection; +use Spiral\TemporalBridge\Connection\SslConnection; use Temporal\Client\ClientOptions; use Temporal\Exception\ExceptionInterceptorInterface; use Temporal\Internal\Interceptor\Interceptor; @@ -21,8 +24,8 @@ * } * * @property array{ - * address: non-empty-string, - * namespace: non-empty-string, + * connection: non-empty-string, + * connections: array, * temporalNamespace: non-empty-string, * defaultWorker: non-empty-string, * workers: array, @@ -35,8 +38,8 @@ final class TemporalConfig extends InjectableConfig public const CONFIG = 'temporal'; protected array $config = [ - 'address' => 'localhost:7233', - 'namespace' => 'App\\Endpoint\\Temporal\\Workflow', + 'connection' => 'default', + 'connections' => [], 'temporalNamespace' => 'default', 'defaultWorker' => WorkerFactoryInterface::DEFAULT_TASK_QUEUE, 'workers' => [], @@ -47,25 +50,41 @@ final class TemporalConfig extends InjectableConfig /** * @return non-empty-string */ - public function getDefaultNamespace(): string + public function getTemporalNamespace(): string { - return $this->config['namespace']; + return $this->config['temporalNamespace']; } - /** - * @return non-empty-string - */ - public function getTemporalNamespace(): string + public function getDefaultConnection(): string { - return $this->config['temporalNamespace']; + return $this->config['connection'] ?? 'default'; + } + + public function getConnection(string $name): Connection + { + if (isset($this->config['connections'][$name])) { + \assert( + $this->config['connections'][$name] instanceof Connection, + 'Connection must be an instance of Connection.', + ); + + return $this->config['connections'][$name]; + } + + + if ($this->config['connections'] === [] && $this->config['address'] !== null) { + return new DsnConnection($this->config['address']); + } + + throw new \InvalidArgumentException(\sprintf('Connection `%s` is not defined.', $name)); } /** - * @return non-empty-string + * @deprecated */ public function getAddress(): string { - return $this->config['address']; + return $this->getConnection($this->getDefaultConnection())->getAddress(); } /** diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php new file mode 100644 index 0000000..204b9f8 --- /dev/null +++ b/src/Connection/Connection.php @@ -0,0 +1,23 @@ +host . ':' . $this->port; + } +} diff --git a/src/Connection/DsnConnection.php b/src/Connection/DsnConnection.php new file mode 100644 index 0000000..78a428a --- /dev/null +++ b/src/Connection/DsnConnection.php @@ -0,0 +1,15 @@ + 'default', + 'connections' => [ + 'default' => new DsnConnection( + address: 'localhost:7233', + ), + 'ssl' => new SslConnection( + address: 'ssl:7233', + crt: '/path/to/crt', + clientKey: '/path/to/clientKey', + clientPem: '/path/to/clientPem', + overrideServerName: 'overrideServerName', + ), + ], +]; diff --git a/tests/app/src/config/temporal.php b/tests/app/src/config/temporal.php deleted file mode 100644 index 1d62656..0000000 --- a/tests/app/src/config/temporal.php +++ /dev/null @@ -1,7 +0,0 @@ - 'Spiral\\TemporalBridge\\Tests\\App\\Workflow', -]; diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index 5a4b7e7..16959f9 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -18,6 +18,8 @@ use Spiral\TemporalBridge\WorkerFactoryInterface; use Spiral\TemporalBridge\WorkersRegistry; use Spiral\TemporalBridge\WorkersRegistryInterface; +use Spiral\Testing\Attribute\Env; +use Temporal\Api\Workflowservice\V1\WorkflowServiceClient; use Temporal\Client\GRPC\ServiceClient; use Temporal\Client\GRPC\ServiceClientInterface; use Temporal\Client\ScheduleClient; @@ -63,7 +65,7 @@ public function testWorkerFactory(): void { $this->assertContainerBoundAsSingleton( WorkerFactoryInterface::class, - WorkerFactory::class + WorkerFactory::class, ); } @@ -107,6 +109,47 @@ public function testPipelineProvider(): void ); } + #[Env('TEMPORAL_CONNECTION', 'default')] + public function testConnection(): void + { + $client = $this->getContainer()->get(ServiceClientInterface::class); + + $refl = new \ReflectionClass($client); + $baseClient = $refl->getParentClass(); + $property = $baseClient->getProperty('workflowService'); + + $property->setAccessible(true); + /** @var WorkflowServiceClient $stub */ + $stub = $property->getValue($client); + + $this->assertSame('localhost:7233', $stub->getTarget()); + } + + #[Env('TEMPORAL_CONNECTION', 'ssl')] + public function testSslConnection(): void + { + $client = $this->getContainer()->get(ServiceClientInterface::class); + + $refl = new \ReflectionClass($client); + $baseClient = $refl->getParentClass(); + $property = $baseClient->getProperty('workflowService'); + + $property->setAccessible(true); + /** @var WorkflowServiceClient $stub */ + $stub = $property->getValue($client); + + $this->assertSame('ssl:7233', $stub->getTarget()); + } + + #[Env('TEMPORAL_CONNECTION', 'test')] + public function testNonExistsConnection(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Connection `test` is not defined.'); + + $this->getContainer()->get(ServiceClientInterface::class); + } + public function testAddWorkerOptions(): void { $configs = new ConfigManager($this->createMock(LoaderInterface::class)); diff --git a/tests/src/Config/TemporalConfigTest.php b/tests/src/Config/TemporalConfigTest.php index 67bf00a..c4b032d 100644 --- a/tests/src/Config/TemporalConfigTest.php +++ b/tests/src/Config/TemporalConfigTest.php @@ -5,6 +5,8 @@ namespace Spiral\TemporalBridge\Tests\Config; use Spiral\TemporalBridge\Config\TemporalConfig; +use Spiral\TemporalBridge\Connection\DsnConnection; +use Spiral\TemporalBridge\Connection\SslConnection; use Spiral\TemporalBridge\Tests\TestCase; use Temporal\Client\ClientOptions; use Temporal\Worker\WorkerFactoryInterface; @@ -12,22 +14,6 @@ final class TemporalConfigTest extends TestCase { - public function testGetsDefaultNamespace(): void - { - $config = new TemporalConfig([ - 'namespace' => 'foo' - ]); - - $this->assertSame('foo', $config->getDefaultNamespace()); - } - - public function testGetsDefaultNamespaceIfItNotSet(): void - { - $config = new TemporalConfig([]); - - $this->assertSame('App\\Endpoint\\Temporal\\Workflow', $config->getDefaultNamespace()); - } - public function testGetsDefaultTemporalNamespaceIfItNotSet(): void { $config = new TemporalConfig([]); @@ -44,26 +30,49 @@ public function testGetsDefaultTemporalNamespace(): void $this->assertSame('foo', $config->getTemporalNamespace()); } - public function testGetsAddress(): void + public function testGetConnectionFromAddress(): void { $config = new TemporalConfig([ - 'address' => 'localhost:1111' + 'address' => 'localhost:1111', ]); - $this->assertSame('localhost:1111', $config->getAddress()); + $connection = $config->getConnection('default'); + $this->assertSame(DsnConnection::class, $connection::class); + + $this->assertSame(1111, $connection->port); + $this->assertSame('localhost', $connection->host); } - public function testGetsAddressIfItNotSet(): void + public function testGetSslConnection(): void { - $config = new TemporalConfig([]); + $config = new TemporalConfig([ + 'connections' => [ + 'default' => new SslConnection( + address: 'localhost:2222', + crt: 'crt', + clientKey: 'clientKey', + clientPem: 'clientPem', + overrideServerName: 'overrideServerName', + ), + ], + ]); + + $connection = $config->getConnection('default'); + + $this->assertSame(SslConnection::class, $connection::class); - $this->assertSame('localhost:7233', $config->getAddress()); + $this->assertSame(2222, $connection->port); + $this->assertSame('localhost', $connection->host); + $this->assertSame('crt', $connection->crt); + $this->assertSame('clientKey', $connection->clientKey); + $this->assertSame('clientPem', $connection->clientPem); + $this->assertSame('overrideServerName', $connection->overrideServerName); } public function testGetsDefaultWorker(): void { $config = new TemporalConfig([ - 'defaultWorker' => 'some-worker' + 'defaultWorker' => 'some-worker', ]); $this->assertSame('some-worker', $config->getDefaultWorker()); @@ -86,23 +95,23 @@ public function testGetsWorkers(): void ], 'withInterceptors' => [ 'interceptors' => [ - 'foo' + 'foo', ], ], 'withExceptionInterceptor' => [ - 'exception_interceptor' => 'bar' + 'exception_interceptor' => 'bar', ], 'all' => [ 'options' => WorkerOptions::new(), 'interceptors' => [ - 'foo' + 'foo', ], - 'exception_interceptor' => 'bar' + 'exception_interceptor' => 'bar', ], ]; $config = new TemporalConfig([ - 'workers' => $workers + 'workers' => $workers, ]); $this->assertSame($workers, $config->getWorkers()); From 5af4962b596c0eb69b8c25b4b41e2c0a15cfabac Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 10:38:33 +0400 Subject: [PATCH 02/19] fix --- tests/app/config/temporal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/config/temporal.php b/tests/app/config/temporal.php index 837cb13..7ab7fcb 100644 --- a/tests/app/config/temporal.php +++ b/tests/app/config/temporal.php @@ -6,7 +6,7 @@ use Spiral\TemporalBridge\Connection\SslConnection; return [ - 'connection' => 'default', + 'connection' => env('TEMPORAL_CONNECTION', 'default'), 'connections' => [ 'default' => new DsnConnection( address: 'localhost:7233', From 2f65f78fc3c175d4b634015589d47d3aa12aaccd Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 10:43:17 +0400 Subject: [PATCH 03/19] fix psalm issues --- src/Config/TemporalConfig.php | 12 ++++-------- src/Connection/DsnConnection.php | 5 +++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index 04b7d63..f6f40e9 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -24,6 +24,7 @@ * } * * @property array{ + * address?: non-empty-string|null, * connection: non-empty-string, * connections: array, * temporalNamespace: non-empty-string, @@ -63,17 +64,12 @@ public function getDefaultConnection(): string public function getConnection(string $name): Connection { if (isset($this->config['connections'][$name])) { - \assert( - $this->config['connections'][$name] instanceof Connection, - 'Connection must be an instance of Connection.', - ); - return $this->config['connections'][$name]; } - - if ($this->config['connections'] === [] && $this->config['address'] !== null) { - return new DsnConnection($this->config['address']); + $address = $this->config['address'] ?? null; + if ($this->config['connections'] === [] && $address !== null) { + return new DsnConnection(address: $address); } throw new \InvalidArgumentException(\sprintf('Connection `%s` is not defined.', $name)); diff --git a/src/Connection/DsnConnection.php b/src/Connection/DsnConnection.php index 78a428a..92efc17 100644 --- a/src/Connection/DsnConnection.php +++ b/src/Connection/DsnConnection.php @@ -6,10 +6,15 @@ class DsnConnection extends Connection { + /** + * @param non-empty-string $address Address in format host:port + */ public function __construct( public readonly string $address, ) { [$host, $port] = \explode(':', $address); + \assert($host !== '', 'Host must be a non-empty string'); + parent::__construct($host, (int)$port); } } From 2a9d04bc3b32895980385a4ba97822255c308f93 Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 11:39:20 +0400 Subject: [PATCH 04/19] Added TemporalCloudConnection class and deprecated 'address' config key --- src/Bootloader/TemporalBridgeBootloader.php | 10 +++---- src/Config/TemporalConfig.php | 17 ++++++++---- src/Connection/Connection.php | 12 +-------- src/Connection/DsnConnection.php | 20 -------------- src/Connection/SslConnection.php | 6 ++--- src/Connection/TemporalCloudConnection.php | 30 +++++++++++++++++++++ tests/app/config/temporal.php | 10 +++++-- tests/src/Config/TemporalConfigTest.php | 9 +++---- 8 files changed, 63 insertions(+), 51 deletions(-) delete mode 100644 src/Connection/DsnConnection.php create mode 100644 src/Connection/TemporalCloudConnection.php diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index 81495cb..95d5f3e 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -16,8 +16,8 @@ use Spiral\RoadRunnerBridge\Bootloader\RoadRunnerBootloader; use Spiral\TemporalBridge\Commands; use Spiral\TemporalBridge\Config\TemporalConfig; +use Spiral\TemporalBridge\Connection\Connection; use Spiral\TemporalBridge\Connection\SslConnection; -use Spiral\TemporalBridge\Connection\DsnConnection; use Spiral\TemporalBridge\DeclarationLocator; use Spiral\TemporalBridge\DeclarationLocatorInterface; use Spiral\TemporalBridge\Dispatcher; @@ -125,7 +125,7 @@ protected function initConfig(EnvironmentInterface $env): void [ 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => new DsnConnection(address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233')), + 'default' => new Connection(address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233')), ], 'defaultWorker' => (string)$env->get( 'TEMPORAL_TASK_QUEUE', @@ -193,15 +193,15 @@ protected function initServiceClient(TemporalConfig $config): ServiceClientInter if ($connection instanceof SslConnection) { return ServiceClient::createSSL( - address: $connection->getAddress(), - crt: $connection->crt, + address: $connection->address, + crt: (string) $connection->crt, clientKey: $connection->clientKey, clientPem: $connection->clientPem, overrideServerName: $connection->overrideServerName, ); } - return ServiceClient::create(address: $connection->getAddress()); + return ServiceClient::create(address: $connection->address); } protected function initScheduleClient( diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index f6f40e9..c3f28d7 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -63,13 +63,20 @@ public function getDefaultConnection(): string public function getConnection(string $name): Connection { - if (isset($this->config['connections'][$name])) { - return $this->config['connections'][$name]; + // Legacy support. Will be removed in further versions. + // If you read this, please remove address from your configuration and use connections instead. + $address = $this->config['address'] ?? null; + if ($address !== null) { + \trigger_deprecation( + 'spiral/temporal-bridge', + '3.1.0', + 'Using `address` is deprecated, use `connections` instead.', + ); + return new Connection(address: $address); } - $address = $this->config['address'] ?? null; - if ($this->config['connections'] === [] && $address !== null) { - return new DsnConnection(address: $address); + if (isset($this->config['connections'][$name])) { + return $this->config['connections'][$name]; } throw new \InvalidArgumentException(\sprintf('Connection `%s` is not defined.', $name)); diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php index 204b9f8..78bf446 100644 --- a/src/Connection/Connection.php +++ b/src/Connection/Connection.php @@ -6,18 +6,8 @@ class Connection { - /** - * @param non-empty-string $host - * @param int $port - */ public function __construct( - public readonly string $host, - public readonly int $port, + public readonly string $address, ) { } - - public function getAddress(): string - { - return $this->host . ':' . $this->port; - } } diff --git a/src/Connection/DsnConnection.php b/src/Connection/DsnConnection.php deleted file mode 100644 index 92efc17..0000000 --- a/src/Connection/DsnConnection.php +++ /dev/null @@ -1,20 +0,0 @@ - env('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => new DsnConnection( + 'default' => new Connection( address: 'localhost:7233', ), 'ssl' => new SslConnection( @@ -18,5 +19,10 @@ clientPem: '/path/to/clientPem', overrideServerName: 'overrideServerName', ), + 'temporal_cloud' => new TemporalCloudConnection( + address: 'ssl:7233', + clientKey: '/path/to/clientKey', + clientPem: '/path/to/clientPem', + ), ], ]; diff --git a/tests/src/Config/TemporalConfigTest.php b/tests/src/Config/TemporalConfigTest.php index c4b032d..11b5eeb 100644 --- a/tests/src/Config/TemporalConfigTest.php +++ b/tests/src/Config/TemporalConfigTest.php @@ -5,6 +5,7 @@ namespace Spiral\TemporalBridge\Tests\Config; use Spiral\TemporalBridge\Config\TemporalConfig; +use Spiral\TemporalBridge\Connection\Connection; use Spiral\TemporalBridge\Connection\DsnConnection; use Spiral\TemporalBridge\Connection\SslConnection; use Spiral\TemporalBridge\Tests\TestCase; @@ -37,10 +38,9 @@ public function testGetConnectionFromAddress(): void ]); $connection = $config->getConnection('default'); - $this->assertSame(DsnConnection::class, $connection::class); + $this->assertSame(Connection::class, $connection::class); - $this->assertSame(1111, $connection->port); - $this->assertSame('localhost', $connection->host); + $this->assertSame('localhost:1111', $connection->address); } public function testGetSslConnection(): void @@ -61,8 +61,7 @@ public function testGetSslConnection(): void $this->assertSame(SslConnection::class, $connection::class); - $this->assertSame(2222, $connection->port); - $this->assertSame('localhost', $connection->host); + $this->assertSame('localhost:2222', $connection->address); $this->assertSame('crt', $connection->crt); $this->assertSame('clientKey', $connection->clientKey); $this->assertSame('clientPem', $connection->clientPem); From 5796ee420256b008c74f1651cbf2265e84570cbb Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 11:42:29 +0400 Subject: [PATCH 05/19] fix --- src/Config/TemporalConfig.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index c3f28d7..f874a3e 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -67,10 +67,9 @@ public function getConnection(string $name): Connection // If you read this, please remove address from your configuration and use connections instead. $address = $this->config['address'] ?? null; if ($address !== null) { - \trigger_deprecation( - 'spiral/temporal-bridge', - '3.1.0', + \trigger_error( 'Using `address` is deprecated, use `connections` instead.', + \E_USER_DEPRECATED, ); return new Connection(address: $address); } From e395aae54acb630b4d0781c670ffd583f11dcc28 Mon Sep 17 00:00:00 2001 From: butschster Date: Fri, 22 Mar 2024 11:42:41 +0400 Subject: [PATCH 06/19] fix --- src/Config/TemporalConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index f874a3e..df89a67 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -86,7 +86,7 @@ public function getConnection(string $name): Connection */ public function getAddress(): string { - return $this->getConnection($this->getDefaultConnection())->getAddress(); + return $this->getConnection($this->getDefaultConnection())->address; } /** From 8f0044db3a27c800b9a27243382f104d7bd2079a Mon Sep 17 00:00:00 2001 From: Pavel Buchnev Date: Tue, 26 Mar 2024 11:55:51 +0400 Subject: [PATCH 07/19] Update src/Connection/Connection.php Co-authored-by: Aleksei Gagarin --- src/Connection/Connection.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php index 78bf446..473c618 100644 --- a/src/Connection/Connection.php +++ b/src/Connection/Connection.php @@ -6,6 +6,9 @@ class Connection { + /** + * @param non-empty-string $address + */ public function __construct( public readonly string $address, ) { From a8f3e304d5e91948caada2c11d947f9714208873 Mon Sep 17 00:00:00 2001 From: Pavel Buchnev Date: Tue, 26 Mar 2024 11:55:58 +0400 Subject: [PATCH 08/19] Update src/Connection/SslConnection.php Co-authored-by: Aleksei Gagarin --- src/Connection/SslConnection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Connection/SslConnection.php b/src/Connection/SslConnection.php index 0ad35c0..f85f0c3 100644 --- a/src/Connection/SslConnection.php +++ b/src/Connection/SslConnection.php @@ -8,9 +8,9 @@ class SslConnection extends Connection { /** * @param non-empty-string $address - * @param string $crt Full path to the certificate file (Default: null) - * @param string|null $clientKey Full path to the client key file (Default: null) - * @param string|null $clientPem Full path to the client pem file (Default: null) + * @param non-empty-string $crt Full path to the certificate file + * @param non-empty-string|null $clientKey Full path to the client key file + * @param non-empty-string|null $clientPem Full path to the client pem file * @param string|null $overrideServerName */ public function __construct( From 5b842867f9ca55eccbb886df2165aede9852de86 Mon Sep 17 00:00:00 2001 From: Pavel Buchnev Date: Tue, 26 Mar 2024 11:56:03 +0400 Subject: [PATCH 09/19] Update src/Connection/TemporalCloudConnection.php Co-authored-by: Aleksei Gagarin --- src/Connection/TemporalCloudConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Connection/TemporalCloudConnection.php b/src/Connection/TemporalCloudConnection.php index 7ecb45a..d64c284 100644 --- a/src/Connection/TemporalCloudConnection.php +++ b/src/Connection/TemporalCloudConnection.php @@ -13,8 +13,8 @@ final class TemporalCloudConnection extends SslConnection { /** * @param non-empty-string $address - * @param non-empty-string $clientKey Full path to the client key file. Required. - * @param non-empty-string $clientPem Full path to the client pem file. Required. + * @param non-empty-string $clientKey Full path to the client key file. + * @param non-empty-string $clientPem Full path to the client pem file. */ public function __construct( string $address, From b6f78918a80337bf7e3c549a66fb3cb2df98e4c5 Mon Sep 17 00:00:00 2001 From: Pavel Buchnev Date: Mon, 20 May 2024 11:17:30 +0400 Subject: [PATCH 10/19] Update src/Connection/Connection.php Co-authored-by: Aleksei Gagarin --- src/Connection/Connection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php index 473c618..4b4e27b 100644 --- a/src/Connection/Connection.php +++ b/src/Connection/Connection.php @@ -8,6 +8,10 @@ class Connection { /** * @param non-empty-string $address + * + * ```php + * new Connection('localhost:7233'); + * ``` */ public function __construct( public readonly string $address, From 6a72992d2c2be2ed885f80044eabcbe71df4a57e Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 28 Aug 2024 13:55:54 +0400 Subject: [PATCH 11/19] update composer.json --- composer.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 1104a89..c41cd52 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,19 @@ "chat": "https://discord.gg/V6EK4he" }, "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/spiral" + } + ], "authors": [ { "name": "Anton Titov (wolfy-j)", "email": "wolfy-j@spiralscout.com" }, { - "name": "Pavel Butchnev (butschster)", + "name": "Pavel Buchnev (butschster)", "email": "pavel.buchnev@spiralscout.com" }, { @@ -36,7 +42,7 @@ } ], "require": { - "php": "^8.1", + "php": ">=8.1", "spiral/boot": "^3.0", "spiral/attributes": "^2.8 || ^3.0", "spiral/tokenizer": "^3.0", @@ -60,12 +66,6 @@ "Spiral\\TemporalBridge\\Tests\\": "tests/src" } }, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/roadrunner-server" - } - ], "scripts": { "test": "vendor/bin/phpunit", "psalm": "vendor/bin/psalm --no-cache --config=psalm.xml ./src" From bdd37473400fe8fbd2fc891690ab32f695afd54b Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 28 Aug 2024 20:11:18 +0400 Subject: [PATCH 12/19] Replace Connection classes with single ConnectionConfig class --- src/Bootloader/TemporalBridgeBootloader.php | 26 +++---- src/Config/ConnectionConfig.php | 77 +++++++++++++++++++ src/Config/TemporalConfig.php | 9 +-- src/Connection/Connection.php | 20 ----- src/Connection/SslConnection.php | 25 ------ src/Connection/TemporalCloudConnection.php | 30 -------- tests/app/config/temporal.php | 21 +++-- .../TemporalBridgeBootloaderTest.php | 2 +- tests/src/Config/TemporalConfigTest.php | 25 +++--- 9 files changed, 111 insertions(+), 124 deletions(-) create mode 100644 src/Config/ConnectionConfig.php delete mode 100644 src/Connection/Connection.php delete mode 100644 src/Connection/SslConnection.php delete mode 100644 src/Connection/TemporalCloudConnection.php diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index 210fef6..e421b2f 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -15,9 +15,8 @@ use Spiral\Core\FactoryInterface; use Spiral\RoadRunnerBridge\Bootloader\RoadRunnerBootloader; use Spiral\TemporalBridge\Commands; +use Spiral\TemporalBridge\Config\ConnectionConfig; use Spiral\TemporalBridge\Config\TemporalConfig; -use Spiral\TemporalBridge\Connection\Connection; -use Spiral\TemporalBridge\Connection\SslConnection; use Spiral\TemporalBridge\DeclarationLocator; use Spiral\TemporalBridge\DeclarationLocatorInterface; use Spiral\TemporalBridge\DeclarationRegistryInterface; @@ -156,11 +155,11 @@ protected function initConfig(EnvironmentInterface $env): void $this->config->setDefaults( TemporalConfig::CONFIG, [ - // 'address' => $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), - // 'namespace' => 'App\\Endpoint\\Temporal\\Workflow', 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => new Connection(address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233')), + 'default' => ConnectionConfig::createInsecure( + address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), + ), ], 'defaultWorker' => (string)$env->get( 'TEMPORAL_TASK_QUEUE', @@ -176,17 +175,14 @@ protected function initServiceClient(TemporalConfig $config): ServiceClientInter { $connection = $config->getConnection($config->getDefaultConnection()); - if ($connection instanceof SslConnection) { - return ServiceClient::createSSL( + return $connection->secure + ? ServiceClient::createSSL( address: $connection->address, - crt: $connection->crt, - clientKey: $connection->clientKey, - clientPem: $connection->clientPem, - overrideServerName: $connection->overrideServerName, - ); - } - - return ServiceClient::create(address: $connection->address); + crt: $connection->rootCerts, + clientKey: $connection->privateKey, + clientPem: $connection->certChain, + ) + : ServiceClient::create(address: $connection->address); } protected function initPipelineProvider(TemporalConfig $config, FactoryInterface $factory): PipelineProvider diff --git a/src/Config/ConnectionConfig.php b/src/Config/ConnectionConfig.php new file mode 100644 index 0000000..95ca571 --- /dev/null +++ b/src/Config/ConnectionConfig.php @@ -0,0 +1,77 @@ +, + * connections: array, * temporalNamespace: non-empty-string, * defaultWorker: non-empty-string, * workers: array, @@ -62,7 +59,7 @@ public function getDefaultConnection(): string return $this->config['connection'] ?? 'default'; } - public function getConnection(string $name): Connection + public function getConnection(string $name): ConnectionConfig { // Legacy support. Will be removed in further versions. // If you read this, please remove address from your configuration and use connections instead. @@ -72,7 +69,7 @@ public function getConnection(string $name): Connection 'Using `address` is deprecated, use `connections` instead.', \E_USER_DEPRECATED, ); - return new Connection(address: $address); + return ConnectionConfig::createInsecure(address: $address); } if (isset($this->config['connections'][$name])) { diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php deleted file mode 100644 index 4b4e27b..0000000 --- a/src/Connection/Connection.php +++ /dev/null @@ -1,20 +0,0 @@ - env('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => new Connection( + 'default' => ConnectionConfig::createInsecure( address: 'localhost:7233', ), - 'ssl' => new SslConnection( + 'ssl' => ConnectionConfig::createSecure( address: 'ssl:7233', - crt: '/path/to/crt', - clientKey: '/path/to/clientKey', - clientPem: '/path/to/clientPem', - overrideServerName: 'overrideServerName', + rootCerts: '/path/to/crt', + privateKey: '/path/to/clientKey', + certChain: '/path/to/clientPem', ), - 'temporal_cloud' => new TemporalCloudConnection( + 'temporal_cloud' => ConnectionConfig::createCloud( address: 'ssl:7233', - clientKey: '/path/to/clientKey', - clientPem: '/path/to/clientPem', + privateKey: '/path/to/clientKey', + certChain: '/path/to/clientPem', ), ], ]; diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index 16959f9..c2eb152 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -126,7 +126,7 @@ public function testConnection(): void } #[Env('TEMPORAL_CONNECTION', 'ssl')] - public function testSslConnection(): void + public function testSecureConnection(): void { $client = $this->getContainer()->get(ServiceClientInterface::class); diff --git a/tests/src/Config/TemporalConfigTest.php b/tests/src/Config/TemporalConfigTest.php index 11b5eeb..45f9170 100644 --- a/tests/src/Config/TemporalConfigTest.php +++ b/tests/src/Config/TemporalConfigTest.php @@ -4,10 +4,8 @@ namespace Spiral\TemporalBridge\Tests\Config; +use Spiral\TemporalBridge\Config\ConnectionConfig; use Spiral\TemporalBridge\Config\TemporalConfig; -use Spiral\TemporalBridge\Connection\Connection; -use Spiral\TemporalBridge\Connection\DsnConnection; -use Spiral\TemporalBridge\Connection\SslConnection; use Spiral\TemporalBridge\Tests\TestCase; use Temporal\Client\ClientOptions; use Temporal\Worker\WorkerFactoryInterface; @@ -38,7 +36,7 @@ public function testGetConnectionFromAddress(): void ]); $connection = $config->getConnection('default'); - $this->assertSame(Connection::class, $connection::class); + $this->assertSame(ConnectionConfig::class, $connection::class); $this->assertSame('localhost:1111', $connection->address); } @@ -47,25 +45,22 @@ public function testGetSslConnection(): void { $config = new TemporalConfig([ 'connections' => [ - 'default' => new SslConnection( + 'default' => ConnectionConfig::createSecure( address: 'localhost:2222', - crt: 'crt', - clientKey: 'clientKey', - clientPem: 'clientPem', - overrideServerName: 'overrideServerName', + rootCerts: 'crt', + privateKey: 'clientKey', + certChain: 'clientPem', ), ], ]); $connection = $config->getConnection('default'); - $this->assertSame(SslConnection::class, $connection::class); - + $this->assertTrue($connection->secure); $this->assertSame('localhost:2222', $connection->address); - $this->assertSame('crt', $connection->crt); - $this->assertSame('clientKey', $connection->clientKey); - $this->assertSame('clientPem', $connection->clientPem); - $this->assertSame('overrideServerName', $connection->overrideServerName); + $this->assertSame('crt', $connection->rootCerts); + $this->assertSame('clientKey', $connection->privateKey); + $this->assertSame('clientPem', $connection->certChain); } public function testGetsDefaultWorker(): void From 8af5186463b7d3bf94e4fc4f87f71ffa121a2df7 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 28 Aug 2024 21:00:31 +0400 Subject: [PATCH 13/19] Add AuthKey into ConnectionConfig; add github funding --- .github/FUNDING.yml | 3 + src/Config/ConnectionConfig.php | 30 ++++++ .../TemporalBridgeBootloaderTest.php | 30 +++--- tests/src/Config/ConnectionConfigTest.php | 94 +++++++++++++++++++ 4 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 tests/src/Config/ConnectionConfigTest.php diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..ac8bfad --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: spiral diff --git a/src/Config/ConnectionConfig.php b/src/Config/ConnectionConfig.php index 95ca571..de1c088 100644 --- a/src/Config/ConnectionConfig.php +++ b/src/Config/ConnectionConfig.php @@ -25,12 +25,20 @@ */ final class ConnectionConfig { + /** + * @param non-empty-string $address + * @param non-empty-string|null $rootCerts + * @param non-empty-string|null $privateKey + * @param non-empty-string|null $certChain + * @param non-empty-string|\Stringable|null $authToken + */ private function __construct( public readonly string $address, public readonly bool $secure = false, public readonly ?string $rootCerts = null, public readonly ?string $privateKey = null, public readonly ?string $certChain = null, + public readonly string|\Stringable|null $authToken = null, ) {} /** @@ -74,4 +82,26 @@ public static function createCloud( ): self { return new self($address, true, null, $privateKey, $certChain); } + + /** + * Set the authentication token for the service client. + * + * This is the equivalent of providing an "Authorization" header with "Bearer " + the given key. + * This will overwrite any "Authorization" header that may be on the context before each request to the + * Temporal service. + * You may pass your own {@see \Stringable} implementation to be able to change the key dynamically. + * + * @param non-empty-string|\Stringable|null $authToken + */ + public function withAuthKey(string|\Stringable|null $authToken): self + { + return new self( + $this->address, + $this->secure, + $this->rootCerts, + $this->privateKey, + $this->certChain, + $authToken, + ); + } } diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index c2eb152..04536f8 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -113,32 +113,26 @@ public function testPipelineProvider(): void public function testConnection(): void { $client = $this->getContainer()->get(ServiceClientInterface::class); + \assert($client instanceof ServiceClientInterface); + $connection = $client->getConnection(); + \assert($connection instanceof \Temporal\Client\GRPC\Connection\Connection); + $workflowService = (fn() => $connection->workflowService)->call($connection); + \assert($workflowService instanceof WorkflowServiceClient); - $refl = new \ReflectionClass($client); - $baseClient = $refl->getParentClass(); - $property = $baseClient->getProperty('workflowService'); - - $property->setAccessible(true); - /** @var WorkflowServiceClient $stub */ - $stub = $property->getValue($client); - - $this->assertSame('localhost:7233', $stub->getTarget()); + $this->assertSame('localhost:7233', $workflowService->getTarget()); } #[Env('TEMPORAL_CONNECTION', 'ssl')] public function testSecureConnection(): void { $client = $this->getContainer()->get(ServiceClientInterface::class); + \assert($client instanceof ServiceClientInterface); + $connection = $client->getConnection(); + \assert($connection instanceof \Temporal\Client\GRPC\Connection\Connection); + $workflowService = (fn() => $connection->workflowService)->call($connection); + \assert($workflowService instanceof WorkflowServiceClient); - $refl = new \ReflectionClass($client); - $baseClient = $refl->getParentClass(); - $property = $baseClient->getProperty('workflowService'); - - $property->setAccessible(true); - /** @var WorkflowServiceClient $stub */ - $stub = $property->getValue($client); - - $this->assertSame('ssl:7233', $stub->getTarget()); + $this->assertSame('ssl:7233', $workflowService->getTarget()); } #[Env('TEMPORAL_CONNECTION', 'test')] diff --git a/tests/src/Config/ConnectionConfigTest.php b/tests/src/Config/ConnectionConfigTest.php new file mode 100644 index 0000000..c665a0c --- /dev/null +++ b/tests/src/Config/ConnectionConfigTest.php @@ -0,0 +1,94 @@ +assertTrue($config->secure); + $this->assertSame('localhost:2222', $config->address); + $this->assertSame('crt', $config->rootCerts); + $this->assertSame('clientKey', $config->privateKey); + $this->assertSame('clientPem', $config->certChain); + } + + public function testCreateInsecure(): void + { + $config = ConnectionConfig::createInsecure( + address: 'localhost:1111', + ); + + $this->assertFalse($config->secure); + $this->assertSame('localhost:1111', $config->address); + } + + public function testCreateCloud(): void + { + $config = ConnectionConfig::createCloud( + address: 'localhost:1111', + privateKey: 'clientKey', + certChain: 'clientPem', + ); + + $this->assertTrue($config->secure); + $this->assertSame('localhost:1111', $config->address); + $this->assertSame('clientKey', $config->privateKey); + $this->assertSame('clientPem', $config->certChain); + } + + public function testWithAuthKey(): void + { + $config = ConnectionConfig::createSecure( + address: 'localhost:1111', + certChain: 'clientPem', + ); + + $newConfig = $config->withAuthKey($key = 'authKey'); + + $this->assertNotSame($config, $newConfig); + $this->assertNull($config->authToken); + $this->assertSame($key, $newConfig->authToken); + } + + public function testWithAuthKeyNull(): void + { + $config = ConnectionConfig::createSecure( + address: 'localhost:1111', + )->withAuthKey('authKey'); + + $newConfig = $config->withAuthKey(null); + + $this->assertNotSame($config, $newConfig); + $this->assertNotNull($config->authToken); + $this->assertNull($newConfig->authToken); + } + + public function testWithAuthKeyStringable(): void + { + $config = ConnectionConfig::createSecure( + address: 'localhost:1111', + )->withAuthKey( + $key = new class() implements \Stringable { + public function __toString(): string + { + return 'authKey'; + } + } + ); + + $this->assertSame($key, $config->authToken); + } +} From 48e7016dcd6b542b30c35f44c06cd14ac1db1188 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 28 Aug 2024 21:05:23 +0400 Subject: [PATCH 14/19] Fix tests --- tests/src/Bootloader/TemporalBridgeBootloaderTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index 04536f8..da33b58 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -119,7 +119,8 @@ public function testConnection(): void $workflowService = (fn() => $connection->workflowService)->call($connection); \assert($workflowService instanceof WorkflowServiceClient); - $this->assertSame('localhost:7233', $workflowService->getTarget()); + // It might be dns://localhost:7233 + $this->assertStringContainsString('localhost:7233', $workflowService->getTarget()); } #[Env('TEMPORAL_CONNECTION', 'ssl')] @@ -132,7 +133,8 @@ public function testSecureConnection(): void $workflowService = (fn() => $connection->workflowService)->call($connection); \assert($workflowService instanceof WorkflowServiceClient); - $this->assertSame('ssl:7233', $workflowService->getTarget()); + // It might be dns://ssl:7233 + $this->assertStringContainsString('ssl:7233', $workflowService->getTarget()); } #[Env('TEMPORAL_CONNECTION', 'test')] From bec1ef3b0ee6b676a15493f2743b50698b4c1ab0 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 29 Aug 2024 00:08:22 +0400 Subject: [PATCH 15/19] Format examples in docs --- src/Config/ConnectionConfig.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Config/ConnectionConfig.php b/src/Config/ConnectionConfig.php index de1c088..a175bd6 100644 --- a/src/Config/ConnectionConfig.php +++ b/src/Config/ConnectionConfig.php @@ -9,19 +9,15 @@ * * How to connect to local Temporal server: * - * ```php - * ConnectionConfig::createInsecure('localhost:7233') - * ``` + * ConnectionConfig::createInsecure('localhost:7233'), * * How to connect to Temporal Cloud: * - * ```php - * ConnectionConfig::createCloud( - * address: 'foo-bar-default.baz.tmprl.cloud:7233', - * privateKey: '/my-project.key', - * certChain: '/my-project.pem', - * ) - * ``` + * ConnectionConfig::createCloud( + * address: 'foo-bar-default.baz.tmprl.cloud:7233', + * privateKey: '/my-project.key', + * certChain: '/my-project.pem', + * ), */ final class ConnectionConfig { From d94eb74cefbe30577933df930a1dea01c157217c Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 29 Aug 2024 11:25:11 +0400 Subject: [PATCH 16/19] Separate TlsConfig --- src/Bootloader/TemporalBridgeBootloader.php | 11 ++-- src/Config/ConnectionConfig.php | 61 ++++++++++++--------- src/Config/TemporalConfig.php | 2 +- src/Config/TlsConfig.php | 24 ++++++++ tests/app/config/temporal.php | 6 +- tests/src/Config/ConnectionConfigTest.php | 50 +++++++++-------- tests/src/Config/TemporalConfigTest.php | 15 +++-- 7 files changed, 104 insertions(+), 65 deletions(-) create mode 100644 src/Config/TlsConfig.php diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index e421b2f..acd5946 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -157,7 +157,7 @@ protected function initConfig(EnvironmentInterface $env): void [ 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => ConnectionConfig::createInsecure( + 'default' => ConnectionConfig::create( address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), ), ], @@ -175,12 +175,13 @@ protected function initServiceClient(TemporalConfig $config): ServiceClientInter { $connection = $config->getConnection($config->getDefaultConnection()); - return $connection->secure + return $connection->isSecure() ? ServiceClient::createSSL( address: $connection->address, - crt: $connection->rootCerts, - clientKey: $connection->privateKey, - clientPem: $connection->certChain, + crt: $connection->tlsConfig->rootCerts, + clientKey: $connection->tlsConfig->privateKey, + clientPem: $connection->tlsConfig->certChain, + overrideServerName: $connection->tlsConfig->serverName, ) : ServiceClient::create(address: $connection->address); } diff --git a/src/Config/ConnectionConfig.php b/src/Config/ConnectionConfig.php index a175bd6..f3959a0 100644 --- a/src/Config/ConnectionConfig.php +++ b/src/Config/ConnectionConfig.php @@ -9,7 +9,7 @@ * * How to connect to local Temporal server: * - * ConnectionConfig::createInsecure('localhost:7233'), + * ConnectionConfig::create('localhost:7233'), * * How to connect to Temporal Cloud: * @@ -23,43 +23,32 @@ final class ConnectionConfig { /** * @param non-empty-string $address - * @param non-empty-string|null $rootCerts - * @param non-empty-string|null $privateKey - * @param non-empty-string|null $certChain * @param non-empty-string|\Stringable|null $authToken */ private function __construct( public readonly string $address, - public readonly bool $secure = false, - public readonly ?string $rootCerts = null, - public readonly ?string $privateKey = null, - public readonly ?string $certChain = null, + public readonly ?TlsConfig $tlsConfig = null, public readonly string|\Stringable|null $authToken = null, ) {} /** - * @param non-empty-string $address + * Check if the connection is secure. + * + * @psalm-assert-if-true TlsConfig $this->tlsConfig + * @psalm-assert-if-false null $this->tlsConfig */ - public static function createInsecure( - string $address, - ): self { - return new self($address); + public function isSecure(): bool + { + return $this->tlsConfig !== null; } /** * @param non-empty-string $address - * @param non-empty-string|null $rootCerts Root certificates string or file in PEM format. - * If null provided, default gRPC root certificates are used. - * @param non-empty-string|null $privateKey Client private key string or file in PEM format. - * @param non-empty-string|null $certChain Client certificate chain string or file in PEM format. */ - public static function createSecure( + public static function create( string $address, - ?string $rootCerts = null, - ?string $privateKey = null, - ?string $certChain = null, ): self { - return new self($address, true, $rootCerts, $privateKey, $certChain); + return new self($address); } /** @@ -76,7 +65,28 @@ public static function createCloud( string $privateKey, string $certChain, ): self { - return new self($address, true, null, $privateKey, $certChain); + return new self($address, new TlsConfig(privateKey: $privateKey, certChain: $certChain)); + } + + /** + * Set the TLS configuration for the connection. + * + * @param non-empty-string|null $rootCerts Root certificates string or file in PEM format. + * If null provided, default gRPC root certificates are used. + * @param non-empty-string|null $privateKey Client private key string or file in PEM format. + * @param non-empty-string|null $certChain Client certificate chain string or file in PEM format. + * @param non-empty-string|null $serverName Server name override for TLS verification. + */ + public function withTls( + ?string $rootCerts = null, + ?string $privateKey = null, + ?string $certChain = null, + ?string $serverName = null, + ): self { + return new self( + $this->address, + new TlsConfig($rootCerts, $privateKey, $certChain, $serverName), + ); } /** @@ -93,10 +103,7 @@ public function withAuthKey(string|\Stringable|null $authToken): self { return new self( $this->address, - $this->secure, - $this->rootCerts, - $this->privateKey, - $this->certChain, + $this->tlsConfig, $authToken, ); } diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index b9f30d0..6a19365 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -69,7 +69,7 @@ public function getConnection(string $name): ConnectionConfig 'Using `address` is deprecated, use `connections` instead.', \E_USER_DEPRECATED, ); - return ConnectionConfig::createInsecure(address: $address); + return ConnectionConfig::create(address: $address); } if (isset($this->config['connections'][$name])) { diff --git a/src/Config/TlsConfig.php b/src/Config/TlsConfig.php new file mode 100644 index 0000000..f87c964 --- /dev/null +++ b/src/Config/TlsConfig.php @@ -0,0 +1,24 @@ + env('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => ConnectionConfig::createInsecure( + 'default' => ConnectionConfig::create( address: 'localhost:7233', ), - 'ssl' => ConnectionConfig::createSecure( - address: 'ssl:7233', + 'ssl' => ConnectionConfig::create(address: 'ssl:7233') + ->withTls( rootCerts: '/path/to/crt', privateKey: '/path/to/clientKey', certChain: '/path/to/clientPem', diff --git a/tests/src/Config/ConnectionConfigTest.php b/tests/src/Config/ConnectionConfigTest.php index c665a0c..4f381cc 100644 --- a/tests/src/Config/ConnectionConfigTest.php +++ b/tests/src/Config/ConnectionConfigTest.php @@ -11,27 +11,28 @@ final class ConnectionConfigTest extends TestCase { public function testCreateSecure(): void { - $config = ConnectionConfig::createSecure( + $config = ConnectionConfig::create( address: 'localhost:2222', + )->withTls( rootCerts: 'crt', privateKey: 'clientKey', certChain: 'clientPem', ); - $this->assertTrue($config->secure); + $this->assertTrue($config->isSecure()); $this->assertSame('localhost:2222', $config->address); - $this->assertSame('crt', $config->rootCerts); - $this->assertSame('clientKey', $config->privateKey); - $this->assertSame('clientPem', $config->certChain); + $this->assertSame('crt', $config->tlsConfig->rootCerts); + $this->assertSame('clientKey', $config->tlsConfig->privateKey); + $this->assertSame('clientPem', $config->tlsConfig->certChain); } public function testCreateInsecure(): void { - $config = ConnectionConfig::createInsecure( + $config = ConnectionConfig::create( address: 'localhost:1111', ); - $this->assertFalse($config->secure); + $this->assertFalse($config->isSecure()); $this->assertSame('localhost:1111', $config->address); } @@ -43,16 +44,17 @@ public function testCreateCloud(): void certChain: 'clientPem', ); - $this->assertTrue($config->secure); + $this->assertTrue($config->isSecure()); $this->assertSame('localhost:1111', $config->address); - $this->assertSame('clientKey', $config->privateKey); - $this->assertSame('clientPem', $config->certChain); + $this->assertSame('clientKey', $config->tlsConfig->privateKey); + $this->assertSame('clientPem', $config->tlsConfig->certChain); } public function testWithAuthKey(): void { - $config = ConnectionConfig::createSecure( + $config = ConnectionConfig::create( address: 'localhost:1111', + )->withTls( certChain: 'clientPem', ); @@ -65,29 +67,31 @@ public function testWithAuthKey(): void public function testWithAuthKeyNull(): void { - $config = ConnectionConfig::createSecure( - address: 'localhost:1111', - )->withAuthKey('authKey'); + $config = ConnectionConfig::create(address: 'localhost:1111') + ->withTls() + ->withAuthKey('authKey'); $newConfig = $config->withAuthKey(null); $this->assertNotSame($config, $newConfig); $this->assertNotNull($config->authToken); $this->assertNull($newConfig->authToken); + $this->assertTrue($config->isSecure()); + $this->assertTrue($newConfig->isSecure()); } public function testWithAuthKeyStringable(): void { - $config = ConnectionConfig::createSecure( - address: 'localhost:1111', - )->withAuthKey( - $key = new class() implements \Stringable { - public function __toString(): string - { - return 'authKey'; + $config = ConnectionConfig::create(address: 'localhost:1111') + ->withTls() + ->withAuthKey( + $key = new class() implements \Stringable { + public function __toString(): string + { + return 'authKey'; + } } - } - ); + ); $this->assertSame($key, $config->authToken); } diff --git a/tests/src/Config/TemporalConfigTest.php b/tests/src/Config/TemporalConfigTest.php index 45f9170..a4ebc33 100644 --- a/tests/src/Config/TemporalConfigTest.php +++ b/tests/src/Config/TemporalConfigTest.php @@ -41,26 +41,29 @@ public function testGetConnectionFromAddress(): void $this->assertSame('localhost:1111', $connection->address); } - public function testGetSslConnection(): void + public function testGetTlsConnection(): void { $config = new TemporalConfig([ 'connections' => [ - 'default' => ConnectionConfig::createSecure( + 'default' => ConnectionConfig::create( address: 'localhost:2222', + )->withTls( rootCerts: 'crt', privateKey: 'clientKey', certChain: 'clientPem', + serverName: 'localhost', ), ], ]); $connection = $config->getConnection('default'); - $this->assertTrue($connection->secure); + $this->assertTrue($connection->isSecure()); $this->assertSame('localhost:2222', $connection->address); - $this->assertSame('crt', $connection->rootCerts); - $this->assertSame('clientKey', $connection->privateKey); - $this->assertSame('clientPem', $connection->certChain); + $this->assertSame('crt', $connection->tlsConfig->rootCerts); + $this->assertSame('clientKey', $connection->tlsConfig->privateKey); + $this->assertSame('clientPem', $connection->tlsConfig->certChain); + $this->assertSame('localhost', $connection->tlsConfig->serverName); } public function testGetsDefaultWorker(): void From 38b4118dbb10dd305e23805058b84fc9169626fb Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 29 Aug 2024 20:48:42 +0400 Subject: [PATCH 17/19] Add ClientConfig --- src/Bootloader/TemporalBridgeBootloader.php | 5 +- src/Config/ClientConfig.php | 60 ++++ src/Config/ConnectionConfig.php | 33 +-- src/Config/TemporalConfig.php | 91 ++++-- tests/app/config/temporal.php | 26 +- .../TemporalBridgeBootloaderTest.php | 2 +- .../Scaffolder/ActivityCommandTest.php | 154 +++++----- .../Scaffolder/WorkflowCommandTest.php | 278 +++++++++--------- tests/src/Config/ConnectionConfigTest.php | 24 +- tests/src/Config/TemporalConfigTest.php | 27 +- 10 files changed, 379 insertions(+), 321 deletions(-) create mode 100644 src/Config/ClientConfig.php diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index acd5946..fffbbb1 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -157,7 +157,7 @@ protected function initConfig(EnvironmentInterface $env): void [ 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), 'connections' => [ - 'default' => ConnectionConfig::create( + 'default' => ConnectionConfig::new( address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), ), ], @@ -173,7 +173,8 @@ protected function initConfig(EnvironmentInterface $env): void protected function initServiceClient(TemporalConfig $config): ServiceClientInterface { - $connection = $config->getConnection($config->getDefaultConnection()); + $client = $config->getClientConfig($config->getDefaultClient()); + $connection = $client->connection; return $connection->isSecure() ? ServiceClient::createSSL( diff --git a/src/Config/ClientConfig.php b/src/Config/ClientConfig.php new file mode 100644 index 0000000..11e5dd5 --- /dev/null +++ b/src/Config/ClientConfig.php @@ -0,0 +1,60 @@ +withTls( + * privateKey: '/my-project.key', + * certChain: '/my-project.pem', + * ), + * (new ClientOptions()) + * ->withNamespace('default'), + * Context::default() + * ->withTimeout(4.5) + * ->withRetryOptions( + * RpcRetryOptions::new() + * ->withMaximumAttempts(5) + * ->withInitialInterval(3) + * ->withMaximumInterval(10) + * ->withBackoffCoefficient(1.6) + * ), + * ), + * ), + */ +final class ClientConfig +{ + private function __construct( + public readonly ConnectionConfig $connection, + public readonly ClientOptions $options, + public readonly ContextInterface $context, + ) {} + + /** + * Create a new client configuration. + * + * @param ConnectionConfig $connection + * @param ClientOptions|null $options + * @param ContextInterface|null $context Default Service Client context. + */ + public static function new( + ConnectionConfig $connection, + ?ClientOptions $options = null, + ?ContextInterface $context = null, + ): self { + return new self( + $connection, + $options ?? new ClientOptions(), + $context ?? Context::default(), + ); + } +} diff --git a/src/Config/ConnectionConfig.php b/src/Config/ConnectionConfig.php index f3959a0..002c7a3 100644 --- a/src/Config/ConnectionConfig.php +++ b/src/Config/ConnectionConfig.php @@ -5,19 +5,19 @@ namespace Spiral\TemporalBridge\Config; /** - * Temporal connection configuration. + * Temporal connection and credentials configuration. * * How to connect to local Temporal server: * - * ConnectionConfig::create('localhost:7233'), + * ConnectionConfig::new('localhost:7233'), * * How to connect to Temporal Cloud: * - * ConnectionConfig::createCloud( - * address: 'foo-bar-default.baz.tmprl.cloud:7233', - * privateKey: '/my-project.key', - * certChain: '/my-project.pem', - * ), + * ConnectionConfig::new('foo-bar-default.baz.tmprl.cloud:7233') + * ->withTls( + * privateKey: '/my-project.key', + * certChain: '/my-project.pem', + * ), */ final class ConnectionConfig { @@ -45,29 +45,12 @@ public function isSecure(): bool /** * @param non-empty-string $address */ - public static function create( + public static function new( string $address, ): self { return new self($address); } - /** - * Used to connect to Temporal Cloud. - * - * @link https://docs.temporal.io/cloud/get-started - * - * @param non-empty-string $address - * @param non-empty-string $privateKey Client private key string or file in PEM format. - * @param non-empty-string $certChain Client certificate chain string or file in PEM format. - */ - public static function createCloud( - string $address, - string $privateKey, - string $certChain, - ): self { - return new self($address, new TlsConfig(privateKey: $privateKey, certChain: $certChain)); - } - /** * Set the TLS configuration for the connection. * diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index 6a19365..7c06060 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -21,14 +21,11 @@ * } * * @property array{ - * address?: non-empty-string|null, - * connection: non-empty-string, - * connections: array, - * temporalNamespace: non-empty-string, + * client: non-empty-string, + * clients: array, * defaultWorker: non-empty-string, * workers: array, * interceptors?: TInterceptor[], - * clientOptions?: ClientOptions * } $config */ final class TemporalConfig extends InjectableConfig @@ -37,54 +34,76 @@ final class TemporalConfig extends InjectableConfig public const CONFIG = 'temporal'; protected array $config = [ - 'connection' => 'default', - 'connections' => [], - 'temporalNamespace' => 'default', + 'client' => 'default', + 'clients' => [], 'defaultWorker' => WorkerFactoryInterface::DEFAULT_TASK_QUEUE, 'workers' => [], 'interceptors' => [], - 'clientOptions' => null, ]; + public function __construct(array $config = []) + { + // Legacy support. Will be removed in further versions. + // If you read this, please remove `address` option from your configuration and use `clients` instead. + $address = $config['address'] ?? null; + if ($address !== null) { + \trigger_error( + 'Temporal options `address`, `clientOptions`, `temporalNamespace` are deprecated.', + \E_USER_DEPRECATED, + ); + + // Create a default client configuration from the legacy options. + $namespace = $config['temporalNamespace'] ?? 'default'; + $clientOptions = ($config['clientOptions'] ?? new ClientOptions()) + ->withNamespace($namespace); + + $config['client'] = 'default'; + $config['clients']['default'] = ClientConfig::new( + ConnectionConfig::new(address: $address), + $clientOptions, + ); + } + + parent::__construct($config); + } + /** + * Get default namespace for Temporal client. + * * @return non-empty-string + * + * @deprecated */ public function getTemporalNamespace(): string { - return $this->config['temporalNamespace']; + $client = $this->getDefaultClient(); + return match(true) { + isset($this->config['clients'][$client]) => $this->config['clients'][$client]->options->namespace, + isset($this->config['temporalNamespace']) => $this->config['temporalNamespace'], + default => 'default', + }; } - public function getDefaultConnection(): string + public function getDefaultClient(): string { - return $this->config['connection'] ?? 'default'; + return $this->config['client'] ?? 'default'; } - public function getConnection(string $name): ConnectionConfig + public function getClientConfig(string $name): ClientConfig { - // Legacy support. Will be removed in further versions. - // If you read this, please remove address from your configuration and use connections instead. - $address = $this->config['address'] ?? null; - if ($address !== null) { - \trigger_error( - 'Using `address` is deprecated, use `connections` instead.', - \E_USER_DEPRECATED, - ); - return ConnectionConfig::create(address: $address); - } - - if (isset($this->config['connections'][$name])) { - return $this->config['connections'][$name]; - } - - throw new \InvalidArgumentException(\sprintf('Connection `%s` is not defined.', $name)); + return $this->config['clients'][$name] ?? throw new \InvalidArgumentException( + "Temporal client config `{$name}` is not defined.", + ); } /** + * Get default connection address. + * * @deprecated */ public function getAddress(): string { - return $this->getConnection($this->getDefaultConnection())->address; + return $this->getClientConfig($this->getDefaultClient())->connection->address; } /** @@ -111,8 +130,18 @@ public function getInterceptors(): array return $this->config['interceptors'] ?? []; } + /** + * Get default client options. + * + * @deprecated + */ public function getClientOptions(): ClientOptions { - return $this->config['clientOptions'] ?? (new ClientOptions())->withNamespace($this->getTemporalNamespace()); + $client = $this->getDefaultClient(); + return match(true) { + isset($this->config['clients'][$client]) => $this->config['clients'][$client]->options, + isset($this->config['clientOptions']) => $this->config['clientOptions'], + default => (new ClientOptions())->withNamespace($this->getTemporalNamespace()), + }; } } diff --git a/tests/app/config/temporal.php b/tests/app/config/temporal.php index a921227..20735d1 100644 --- a/tests/app/config/temporal.php +++ b/tests/app/config/temporal.php @@ -2,24 +2,20 @@ declare(strict_types=1); +use Spiral\TemporalBridge\Config\ClientConfig; use Spiral\TemporalBridge\Config\ConnectionConfig; return [ - 'connection' => env('TEMPORAL_CONNECTION', 'default'), - 'connections' => [ - 'default' => ConnectionConfig::create( - address: 'localhost:7233', - ), - 'ssl' => ConnectionConfig::create(address: 'ssl:7233') - ->withTls( - rootCerts: '/path/to/crt', - privateKey: '/path/to/clientKey', - certChain: '/path/to/clientPem', - ), - 'temporal_cloud' => ConnectionConfig::createCloud( - address: 'ssl:7233', - privateKey: '/path/to/clientKey', - certChain: '/path/to/clientPem', + 'client' => env('TEMPORAL_CONNECTION', 'default'), + 'clients' => [ + 'default' => ClientConfig::new(ConnectionConfig::new('localhost:7233')), + 'ssl' => ClientConfig::new( + ConnectionConfig::new(address: 'ssl:7233') + ->withTls( + rootCerts: '/path/to/crt', + privateKey: '/path/to/clientKey', + certChain: '/path/to/clientPem', + ) ), ], ]; diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index da33b58..443bf14 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -141,7 +141,7 @@ public function testSecureConnection(): void public function testNonExistsConnection(): void { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Connection `test` is not defined.'); + $this->expectExceptionMessage('Temporal client config `test` is not defined.'); $this->getContainer()->get(ServiceClientInterface::class); } diff --git a/tests/src/Commands/Scaffolder/ActivityCommandTest.php b/tests/src/Commands/Scaffolder/ActivityCommandTest.php index 143406d..1103805 100644 --- a/tests/src/Commands/Scaffolder/ActivityCommandTest.php +++ b/tests/src/Commands/Scaffolder/ActivityCommandTest.php @@ -25,22 +25,22 @@ public function testGenerate(): void $this->assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Activity/PaymentActivity.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Activity/PaymentActivity.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Activity/PaymentActivity.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Activity/PaymentActivity.php')); $this->assertSame( <<<'PHP' - - */ - #[ActivityMethod(name: 'pay')] - public function pay(): mixed - { - // TODO: Implement activity method - } - - /** - * @return PromiseInterface - */ - #[ActivityMethod(name: 'refund')] - public function refund(): void - { - // TODO: Implement activity method - } - - /** - * @return PromiseInterface - */ - #[ActivityMethod(name: 'getPaymentStatus')] - public function getPaymentStatus(): bool - { - // TODO: Implement activity method - } -} - -PHP, + + */ + #[ActivityMethod(name: 'pay')] + public function pay(): mixed + { + // TODO: Implement activity method + } + + /** + * @return PromiseInterface + */ + #[ActivityMethod(name: 'refund')] + public function refund(): void + { + // TODO: Implement activity method + } + + /** + * @return PromiseInterface + */ + #[ActivityMethod(name: 'getPaymentStatus')] + public function getPaymentStatus(): bool + { + // TODO: Implement activity method + } + } + + PHP, $body, ); return true; diff --git a/tests/src/Commands/Scaffolder/WorkflowCommandTest.php b/tests/src/Commands/Scaffolder/WorkflowCommandTest.php index f1aa4e4..ce6c874 100644 --- a/tests/src/Commands/Scaffolder/WorkflowCommandTest.php +++ b/tests/src/Commands/Scaffolder/WorkflowCommandTest.php @@ -25,29 +25,29 @@ public function testGenerate(): void $this->assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Workflow/PaymentWorkflow.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Workflow/PaymentWorkflow.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Workflow/PaymentWorkflow.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Workflow/PaymentWorkflow.php')); $this->assertSame( <<<'PHP' -assertTrue(\str_ends_with($path, '/app/src/Endpoint/Temporal/Workflow/PaymentWorkflow.php')); $this->assertSame( <<<'PHP' -withTls( rootCerts: 'crt', @@ -28,7 +28,7 @@ public function testCreateSecure(): void public function testCreateInsecure(): void { - $config = ConnectionConfig::create( + $config = ConnectionConfig::new( address: 'localhost:1111', ); @@ -36,23 +36,9 @@ public function testCreateInsecure(): void $this->assertSame('localhost:1111', $config->address); } - public function testCreateCloud(): void - { - $config = ConnectionConfig::createCloud( - address: 'localhost:1111', - privateKey: 'clientKey', - certChain: 'clientPem', - ); - - $this->assertTrue($config->isSecure()); - $this->assertSame('localhost:1111', $config->address); - $this->assertSame('clientKey', $config->tlsConfig->privateKey); - $this->assertSame('clientPem', $config->tlsConfig->certChain); - } - public function testWithAuthKey(): void { - $config = ConnectionConfig::create( + $config = ConnectionConfig::new( address: 'localhost:1111', )->withTls( certChain: 'clientPem', @@ -67,7 +53,7 @@ public function testWithAuthKey(): void public function testWithAuthKeyNull(): void { - $config = ConnectionConfig::create(address: 'localhost:1111') + $config = ConnectionConfig::new(address: 'localhost:1111') ->withTls() ->withAuthKey('authKey'); @@ -82,7 +68,7 @@ public function testWithAuthKeyNull(): void public function testWithAuthKeyStringable(): void { - $config = ConnectionConfig::create(address: 'localhost:1111') + $config = ConnectionConfig::new(address: 'localhost:1111') ->withTls() ->withAuthKey( $key = new class() implements \Stringable { diff --git a/tests/src/Config/TemporalConfigTest.php b/tests/src/Config/TemporalConfigTest.php index a4ebc33..3602c5b 100644 --- a/tests/src/Config/TemporalConfigTest.php +++ b/tests/src/Config/TemporalConfigTest.php @@ -4,6 +4,7 @@ namespace Spiral\TemporalBridge\Tests\Config; +use Spiral\TemporalBridge\Config\ClientConfig; use Spiral\TemporalBridge\Config\ConnectionConfig; use Spiral\TemporalBridge\Config\TemporalConfig; use Spiral\TemporalBridge\Tests\TestCase; @@ -35,28 +36,30 @@ public function testGetConnectionFromAddress(): void 'address' => 'localhost:1111', ]); - $connection = $config->getConnection('default'); - $this->assertSame(ConnectionConfig::class, $connection::class); + $client = $config->getClientConfig('default'); + $this->assertSame(ClientConfig::class, $client::class); - $this->assertSame('localhost:1111', $connection->address); + $this->assertSame('localhost:1111', $client->connection->address); } public function testGetTlsConnection(): void { $config = new TemporalConfig([ - 'connections' => [ - 'default' => ConnectionConfig::create( - address: 'localhost:2222', - )->withTls( - rootCerts: 'crt', - privateKey: 'clientKey', - certChain: 'clientPem', - serverName: 'localhost', + 'clients' => [ + 'default' => ClientConfig::new( + ConnectionConfig::new(address: 'localhost:2222') + ->withTls( + rootCerts: 'crt', + privateKey: 'clientKey', + certChain: 'clientPem', + serverName: 'localhost', + ), ), ], ]); - $connection = $config->getConnection('default'); + $client = $config->getClientConfig('default'); + $connection = $client->connection; $this->assertTrue($connection->isSecure()); $this->assertSame('localhost:2222', $connection->address); From 8bb069f13f6d791441019e00f4279f9a7281914e Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 29 Aug 2024 21:05:33 +0400 Subject: [PATCH 18/19] Fix psalm issues --- psalm.xml | 1 + src/Config/TemporalConfig.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/psalm.xml b/psalm.xml index 96c248e..a538c28 100644 --- a/psalm.xml +++ b/psalm.xml @@ -15,6 +15,7 @@ + diff --git a/src/Config/TemporalConfig.php b/src/Config/TemporalConfig.php index 7c06060..665ed9f 100644 --- a/src/Config/TemporalConfig.php +++ b/src/Config/TemporalConfig.php @@ -26,6 +26,9 @@ * defaultWorker: non-empty-string, * workers: array, * interceptors?: TInterceptor[], + * temporalNamespace?: non-empty-string, + * address?: non-empty-string, + * clientOptions?: ClientOptions * } $config */ final class TemporalConfig extends InjectableConfig From 22401af8d30cbab2b2315e488b10d04c41788030 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Fri, 30 Aug 2024 13:29:02 +0400 Subject: [PATCH 19/19] Apply grpcContext from config; add tests --- src/Bootloader/TemporalBridgeBootloader.php | 16 ++++++++----- tests/app/config/temporal.php | 7 +++++- .../TemporalBridgeBootloaderTest.php | 23 +++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index fffbbb1..fc798ff 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -15,6 +15,7 @@ use Spiral\Core\FactoryInterface; use Spiral\RoadRunnerBridge\Bootloader\RoadRunnerBootloader; use Spiral\TemporalBridge\Commands; +use Spiral\TemporalBridge\Config\ClientConfig; use Spiral\TemporalBridge\Config\ConnectionConfig; use Spiral\TemporalBridge\Config\TemporalConfig; use Spiral\TemporalBridge\DeclarationLocator; @@ -155,10 +156,12 @@ protected function initConfig(EnvironmentInterface $env): void $this->config->setDefaults( TemporalConfig::CONFIG, [ - 'connection' => $env->get('TEMPORAL_CONNECTION', 'default'), - 'connections' => [ - 'default' => ConnectionConfig::new( - address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), + 'client' => $env->get('TEMPORAL_CONNECTION', 'default'), + 'clients' => [ + 'default' => ClientConfig::new( + ConnectionConfig::new( + address: $env->get('TEMPORAL_ADDRESS', '127.0.0.1:7233'), + ), ), ], 'defaultWorker' => (string)$env->get( @@ -166,7 +169,6 @@ protected function initConfig(EnvironmentInterface $env): void TemporalWorkerFactoryInterface::DEFAULT_TASK_QUEUE, ), 'workers' => [], - 'clientOptions' => null, ], ); } @@ -176,7 +178,7 @@ protected function initServiceClient(TemporalConfig $config): ServiceClientInter $client = $config->getClientConfig($config->getDefaultClient()); $connection = $client->connection; - return $connection->isSecure() + $result = $connection->isSecure() ? ServiceClient::createSSL( address: $connection->address, crt: $connection->tlsConfig->rootCerts, @@ -185,6 +187,8 @@ protected function initServiceClient(TemporalConfig $config): ServiceClientInter overrideServerName: $connection->tlsConfig->serverName, ) : ServiceClient::create(address: $connection->address); + + return $result->withContext($client->context); } protected function initPipelineProvider(TemporalConfig $config, FactoryInterface $factory): PipelineProvider diff --git a/tests/app/config/temporal.php b/tests/app/config/temporal.php index 20735d1..b839edd 100644 --- a/tests/app/config/temporal.php +++ b/tests/app/config/temporal.php @@ -4,6 +4,8 @@ use Spiral\TemporalBridge\Config\ClientConfig; use Spiral\TemporalBridge\Config\ConnectionConfig; +use Temporal\Client\ClientOptions; +use Temporal\Client\GRPC\Context; return [ 'client' => env('TEMPORAL_CONNECTION', 'default'), @@ -15,7 +17,10 @@ rootCerts: '/path/to/crt', privateKey: '/path/to/clientKey', certChain: '/path/to/clientPem', - ) + ), + options: (new ClientOptions()) + ->withNamespace('foo-bar'), + context: Context::default()->withMetadata(['foo' => ['bar']]), ), ], ]; diff --git a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php index 443bf14..4eaf7e1 100644 --- a/tests/src/Bootloader/TemporalBridgeBootloaderTest.php +++ b/tests/src/Bootloader/TemporalBridgeBootloaderTest.php @@ -20,6 +20,7 @@ use Spiral\TemporalBridge\WorkersRegistryInterface; use Spiral\Testing\Attribute\Env; use Temporal\Api\Workflowservice\V1\WorkflowServiceClient; +use Temporal\Client\ClientOptions; use Temporal\Client\GRPC\ServiceClient; use Temporal\Client\GRPC\ServiceClientInterface; use Temporal\Client\ScheduleClient; @@ -137,6 +138,28 @@ public function testSecureConnection(): void $this->assertStringContainsString('ssl:7233', $workflowService->getTarget()); } + #[Env('TEMPORAL_CONNECTION', 'ssl')] + public function testContext(): void + { + $client = $this->getContainer()->get(ServiceClientInterface::class); + \assert($client instanceof ServiceClientInterface); + $context = $client->getContext(); + + $this->assertSame(['foo' => ['bar']], $context->getMetadata()); + } + + #[Env('TEMPORAL_CONNECTION', 'ssl')] + public function testClientOptions(): void + { + $client = $this->getContainer()->get(WorkflowClientInterface::class); + \assert($client instanceof WorkflowClientInterface); + + $clientOptions = (fn() => $client->clientOptions)->call($client); + \assert($clientOptions instanceof ClientOptions); + + $this->assertSame('foo-bar', $clientOptions->namespace); + } + #[Env('TEMPORAL_CONNECTION', 'test')] public function testNonExistsConnection(): void {