Skip to content

Commit

Permalink
Enhanced Temporal connection configuration for flexibility and SSL su…
Browse files Browse the repository at this point in the history
…pport

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.
  • Loading branch information
butschster committed Mar 22, 2024
1 parent 38be446 commit 2d262ad
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 52 deletions.
22 changes: 19 additions & 3 deletions src/Bootloader/TemporalBridgeBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
45 changes: 32 additions & 13 deletions src/Config/TemporalConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -21,8 +24,8 @@
* }
*
* @property array{
* address: non-empty-string,
* namespace: non-empty-string,
* connection: non-empty-string,
* connections: array<non-empty-string, Connection>,
* temporalNamespace: non-empty-string,
* defaultWorker: non-empty-string,
* workers: array<non-empty-string, WorkerOptions|TWorker>,
Expand All @@ -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' => [],
Expand All @@ -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();
}

/**
Expand Down
23 changes: 23 additions & 0 deletions src/Connection/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Spiral\TemporalBridge\Connection;

class Connection
{
/**
* @param non-empty-string $host
* @param int $port
*/
public function __construct(
public readonly string $host,
public readonly int $port,
) {
}

public function getAddress(): string
{
return $this->host . ':' . $this->port;
}
}
15 changes: 15 additions & 0 deletions src/Connection/DsnConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Spiral\TemporalBridge\Connection;

class DsnConnection extends Connection
{
public function __construct(
public readonly string $address,
) {
[$host, $port] = \explode(':', $address);
parent::__construct($host, (int)$port);
}
}
25 changes: 25 additions & 0 deletions src/Connection/SslConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Spiral\TemporalBridge\Connection;

final class SslConnection extends DsnConnection
{
/**
* @param non-empty-string $address
* @param string $crt Full path to the certificate file (Default: '')
* @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 string|null $overrideServerName
*/
public function __construct(
string $address,
public readonly string $crt = '',
public readonly ?string $clientKey = null,
public readonly ?string $clientPem = null,
public readonly ?string $overrideServerName = null,
) {
parent::__construct($address);
}
}
22 changes: 22 additions & 0 deletions tests/app/config/temporal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

use Spiral\TemporalBridge\Connection\DsnConnection;
use Spiral\TemporalBridge\Connection\SslConnection;

return [
'connection' => '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',
),
],
];
7 changes: 0 additions & 7 deletions tests/app/src/config/temporal.php

This file was deleted.

45 changes: 44 additions & 1 deletion tests/src/Bootloader/TemporalBridgeBootloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,7 +65,7 @@ public function testWorkerFactory(): void
{
$this->assertContainerBoundAsSingleton(
WorkerFactoryInterface::class,
WorkerFactory::class
WorkerFactory::class,
);
}

Expand Down Expand Up @@ -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));
Expand Down
Loading

0 comments on commit 2d262ad

Please sign in to comment.