Skip to content

Commit

Permalink
Send ApiKey to RoadRunner with WorkerInfo (#539)
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk authored Dec 17, 2024
1 parent a775cb1 commit cc35329
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 40 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"roadrunner-php/roadrunner-api-dto": "^1.9.0",
"roadrunner-php/version-checker": "^1.0",
"spiral/attributes": "^3.1.6",
"spiral/roadrunner": "^2024.1",
"spiral/roadrunner": "^2024.3",
"spiral/roadrunner-cli": "^2.5",
"spiral/roadrunner-kv": "^4.2",
"spiral/roadrunner-worker": "^3.5",
Expand Down
1 change: 1 addition & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,7 @@
<code><![CDATA[new static(
$converter ?? DataConverter::createDefault(),
$rpc ?? Goridge::create(),
$credentials ?? ServiceCredentials::create(),
)]]></code>
</UnsafeInstantiation>
</file>
Expand Down
2 changes: 2 additions & 0 deletions src/Client/GRPC/BaseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ public function withContext(ContextInterface $context): static
* 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.
*
* @link https://docs.temporal.io/cloud/api-keys
*/
public function withAuthKey(\Stringable|string $key): static
{
Expand Down
26 changes: 17 additions & 9 deletions src/Internal/Transport/Router/GetWorkerInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,17 @@
use Temporal\Internal\Declaration\Prototype\WorkflowPrototype;
use Temporal\Internal\Marshaller\MarshallerInterface;
use Temporal\Internal\Repository\RepositoryInterface;
use Temporal\Worker\ServiceCredentials;
use Temporal\Worker\Transport\Command\ServerRequestInterface;
use Temporal\Worker\WorkerInterface;

final class GetWorkerInfo extends Route
{
private RepositoryInterface $queues;
private MarshallerInterface $marshaller;

public function __construct(RepositoryInterface $queues, MarshallerInterface $marshaller)
{
$this->queues = $queues;
$this->marshaller = $marshaller;
}
public function __construct(
private readonly RepositoryInterface $queues,
private readonly MarshallerInterface $marshaller,
private readonly ServiceCredentials $credentials,
) {}

public function handle(ServerRequestInterface $request, array $headers, Deferred $resolver): void
{
Expand Down Expand Up @@ -64,7 +62,7 @@ private function workerToArray(WorkerInterface $worker): array
// ActivityInfo[]
'Activities' => $this->map($worker->getActivities(), $activityMap),
'PhpSdkVersion' => SdkVersion::getSdkVersion(),
'Flags' => (object) [],
'Flags' => (object) $this->prepareFlags(),
];
}

Expand All @@ -78,4 +76,14 @@ private function map(iterable $items, \Closure $map): array

return $result;
}

/**
* @return array<non-empty-string, mixed>
*/
private function prepareFlags(): array
{
return [
'ApiKey' => $this->credentials->apiKey,
];
}
}
47 changes: 47 additions & 0 deletions src/Worker/ServiceCredentials.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Temporal\Worker;

use Temporal\Internal\Traits\CloneWith;

/**
* DTO with credential configuration for connecting RoadRunner to the Temporal service.
*/
final class ServiceCredentials
{
use CloneWith;

public readonly string $apiKey;

private function __construct()
{
$this->apiKey = '';
}

public static function create(): self
{
return new self();
}

/**
* Set the authentication token for API calls.
*
* To update the API key in runtime, call the `UpdateAPIKey` RPC method with the new key:
*
* $result = \Temporal\Worker\Transport\Goridge::create()->call(
* 'temporal.UpdateAPIKey',
* $newApiKey,
* );
*
* @link https://docs.temporal.io/cloud/api-keys
* @since SDK 2.12.0
* @since RoadRunner 2024.3.0
*/
public function withApiKey(string $key): static
{
/** @see self::$apiKey */
return $this->with('apiKey', $key);
}
}
38 changes: 10 additions & 28 deletions src/WorkerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use Temporal\Worker\Environment\Environment;
use Temporal\Worker\Environment\EnvironmentInterface;
use Temporal\Worker\LoopInterface;
use Temporal\Worker\ServiceCredentials;
use Temporal\Worker\Transport\Codec\CodecInterface;
use Temporal\Worker\Transport\Codec\JsonCodec;
use Temporal\Worker\Transport\Codec\ProtoCodec;
Expand Down Expand Up @@ -74,29 +75,10 @@ class WorkerFactory implements WorkerFactoryInterface, LoopInterface
{
use EventEmitterTrait;

/**
* @var string
*/
private const ERROR_MESSAGE_TYPE = 'Received message type must be a string, but %s given';

/**
* @var string
*/
private const ERROR_HEADERS_TYPE = 'Received headers type must be a string, but %s given';

/**
* @var string
*/
private const ERROR_HEADER_NOT_STRING_TYPE = 'Header "%s" argument type must be a string, but %s given';

/**
* @var string
*/
private const ERROR_QUEUE_NOT_FOUND = 'Cannot find a worker for task queue "%s"';

/**
* @var string
*/
private const HEADER_TASK_QUEUE = 'taskQueue';

protected DataConverterInterface $converter;
Expand All @@ -112,7 +94,6 @@ class WorkerFactory implements WorkerFactoryInterface, LoopInterface
protected ClientInterface $client;
protected ServerInterface $server;
protected QueueInterface $responses;
protected RPCConnectionInterface $rpc;

/**
* @var MarshallerInterface<array>
Expand All @@ -123,21 +104,22 @@ class WorkerFactory implements WorkerFactoryInterface, LoopInterface

public function __construct(
DataConverterInterface $dataConverter,
RPCConnectionInterface $rpc,
protected RPCConnectionInterface $rpc,
ServiceCredentials $credentials,
) {
$this->converter = $dataConverter;
$this->rpc = $rpc;

$this->boot();
$this->boot($credentials);
}

public static function create(
?DataConverterInterface $converter = null,
?RPCConnectionInterface $rpc = null,
?ServiceCredentials $credentials = null,
): WorkerFactoryInterface {
return new static(
$converter ?? DataConverter::createDefault(),
$rpc ?? Goridge::create(),
$credentials ?? ServiceCredentials::create(),
);
}

Expand Down Expand Up @@ -237,10 +219,10 @@ protected function createTaskQueue(): RepositoryInterface
return new ArrayRepository();
}

protected function createRouter(): RouterInterface
protected function createRouter(ServiceCredentials $credentials): RouterInterface
{
$router = new Router();
$router->add(new Router\GetWorkerInfo($this->queues, $this->marshaller));
$router->add(new Router\GetWorkerInfo($this->queues, $this->marshaller, $credentials));

return $router;
}
Expand Down Expand Up @@ -269,12 +251,12 @@ protected function createMarshaller(ReaderInterface $reader): MarshallerInterfac
return new Marshaller(new AttributeMapperFactory($reader));
}

private function boot(): void
private function boot(ServiceCredentials $credentials): void
{
$this->reader = $this->createReader();
$this->marshaller = $this->createMarshaller($this->reader);
$this->queues = $this->createTaskQueue();
$this->router = $this->createRouter();
$this->router = $this->createRouter($credentials);
$this->responses = $this->createQueue();
$this->client = $this->createClient();
$this->server = $this->createServer();
Expand Down
6 changes: 5 additions & 1 deletion testing/src/WorkerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Temporal\Internal\ServiceContainer;
use Temporal\Worker\ActivityInvocationCache\ActivityInvocationCacheInterface;
use Temporal\Worker\ActivityInvocationCache\RoadRunnerActivityInvocationCache;
use Temporal\Worker\ServiceCredentials;
use Temporal\Worker\Transport\Goridge;
use Temporal\Worker\Transport\RPCConnectionInterface;
use Temporal\Worker\Worker;
Expand All @@ -27,21 +28,24 @@ public function __construct(
DataConverterInterface $dataConverter,
RPCConnectionInterface $rpc,
ActivityInvocationCacheInterface $activityCache,
ServiceCredentials $credentials,
) {
$this->activityCache = $activityCache;

parent::__construct($dataConverter, $rpc);
parent::__construct($dataConverter, $rpc, $credentials);
}

public static function create(
?DataConverterInterface $converter = null,
?RPCConnectionInterface $rpc = null,
?ServiceCredentials $credentials = null,
?ActivityInvocationCacheInterface $activityCache = null,
): static {
return new static(
$converter ?? DataConverter::createDefault(),
$rpc ?? Goridge::create(),
$activityCache ?? RoadRunnerActivityInvocationCache::create($converter),
$credentials ?? ServiceCredentials::create(),
);
}

Expand Down
3 changes: 2 additions & 1 deletion tests/Unit/Framework/WorkerFactoryMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Temporal\Worker\Environment\Environment;
use Temporal\Worker\Environment\EnvironmentInterface;
use Temporal\Worker\LoopInterface;
use Temporal\Worker\ServiceCredentials;
use Temporal\Worker\Transport\Codec\CodecInterface;
use Temporal\Worker\Transport\Command\ServerRequestInterface;
use Temporal\Worker\Transport\Command\ServerResponseInterface;
Expand Down Expand Up @@ -183,7 +184,7 @@ private function createReader(): ReaderInterface
private function createRouter(): RouterInterface
{
$router = new Router();
$router->add(new Router\GetWorkerInfo($this->queues, $this->marshaller));
$router->add(new Router\GetWorkerInfo($this->queues, $this->marshaller, ServiceCredentials::create()));

return $router;
}
Expand Down
22 changes: 22 additions & 0 deletions tests/Unit/Worker/ServiceCredentialsTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Temporal\Tests\Unit\Worker;

use PHPUnit\Framework\TestCase;
use Temporal\Worker\ServiceCredentials;

class ServiceCredentialsTestCase extends TestCase
{
public function testWithApiKeyImmutability()
{
$dto = ServiceCredentials::create();

$new = $dto->withApiKey('test');

$this->assertNotSame($dto, $new);
$this->assertSame('test', $new->apiKey);
$this->assertSame('', $dto->apiKey);
}
}

0 comments on commit cc35329

Please sign in to comment.