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); + } +}