diff --git a/composer.json b/composer.json
index 8009b87d..a3e4f7d9 100644
--- a/composer.json
+++ b/composer.json
@@ -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",
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 9492f410..bf156c41 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1477,6 +1477,7 @@
diff --git a/src/Client/GRPC/BaseClient.php b/src/Client/GRPC/BaseClient.php
index aec71544..0bf0223c 100644
--- a/src/Client/GRPC/BaseClient.php
+++ b/src/Client/GRPC/BaseClient.php
@@ -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
{
diff --git a/src/Internal/Transport/Router/GetWorkerInfo.php b/src/Internal/Transport/Router/GetWorkerInfo.php
index 8c1e86f8..70925727 100644
--- a/src/Internal/Transport/Router/GetWorkerInfo.php
+++ b/src/Internal/Transport/Router/GetWorkerInfo.php
@@ -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
{
@@ -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(),
];
}
@@ -78,4 +76,14 @@ private function map(iterable $items, \Closure $map): array
return $result;
}
+
+ /**
+ * @return array
+ */
+ private function prepareFlags(): array
+ {
+ return [
+ 'ApiKey' => $this->credentials->apiKey,
+ ];
+ }
}
diff --git a/src/Worker/ServiceCredentials.php b/src/Worker/ServiceCredentials.php
new file mode 100644
index 00000000..536f72bf
--- /dev/null
+++ b/src/Worker/ServiceCredentials.php
@@ -0,0 +1,47 @@
+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);
+ }
+}
diff --git a/src/WorkerFactory.php b/src/WorkerFactory.php
index 43b170dd..b2629dcd 100644
--- a/src/WorkerFactory.php
+++ b/src/WorkerFactory.php
@@ -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;
@@ -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;
@@ -112,7 +94,6 @@ class WorkerFactory implements WorkerFactoryInterface, LoopInterface
protected ClientInterface $client;
protected ServerInterface $server;
protected QueueInterface $responses;
- protected RPCConnectionInterface $rpc;
/**
* @var MarshallerInterface
@@ -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(),
);
}
@@ -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;
}
@@ -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();
diff --git a/testing/src/WorkerFactory.php b/testing/src/WorkerFactory.php
index 5def52ef..bef26305 100644
--- a/testing/src/WorkerFactory.php
+++ b/testing/src/WorkerFactory.php
@@ -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;
@@ -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(),
);
}
diff --git a/tests/Unit/Framework/WorkerFactoryMock.php b/tests/Unit/Framework/WorkerFactoryMock.php
index 9b91b12a..d181f2eb 100644
--- a/tests/Unit/Framework/WorkerFactoryMock.php
+++ b/tests/Unit/Framework/WorkerFactoryMock.php
@@ -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;
@@ -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;
}
diff --git a/tests/Unit/Worker/ServiceCredentialsTestCase.php b/tests/Unit/Worker/ServiceCredentialsTestCase.php
new file mode 100644
index 00000000..d817354d
--- /dev/null
+++ b/tests/Unit/Worker/ServiceCredentialsTestCase.php
@@ -0,0 +1,22 @@
+withApiKey('test');
+
+ $this->assertNotSame($dto, $new);
+ $this->assertSame('test', $new->apiKey);
+ $this->assertSame('', $dto->apiKey);
+ }
+}