diff --git a/Gax/src/GapicClientTrait.php b/Gax/src/GapicClientTrait.php index 6cf858f21d16..77a20e4be2d6 100644 --- a/Gax/src/GapicClientTrait.php +++ b/Gax/src/GapicClientTrait.php @@ -32,7 +32,7 @@ namespace Google\ApiCore; -use Google\ApiCore\LongRunning\OperationsClient; +use Google\ApiCore\LongRunning\OperationsClient as DeprecatedOperationsClient; use Google\ApiCore\Middleware\CredentialsWrapperMiddleware; use Google\ApiCore\Middleware\FixedHeaderMiddleware; use Google\ApiCore\Middleware\OperationsMiddleware; @@ -50,6 +50,7 @@ use Google\ApiCore\Transport\RestTransport; use Google\ApiCore\Transport\TransportInterface; use Google\Auth\FetchAuthTokenInterface; +use Google\LongRunning\Client\OperationsClient; use Google\LongRunning\Operation; use Google\Protobuf\Internal\Message; use GuzzleHttp\Promise\PromiseInterface; @@ -174,7 +175,7 @@ public static function getServiceScopes(): array * * @experimental */ - public function close() + public function close(): void { $this->transport->close(); } @@ -454,7 +455,7 @@ private function createTransport( /** * @param array $options - * @return OperationsClient + * @return DeprecatedOperationsClient|OperationsClient|object */ private function createOperationsClient(array $options) { @@ -470,8 +471,11 @@ private function createOperationsClient(array $options) } // operationsClientClass option + $defaultClass = $this->isBackwardsCompatibilityMode() + ? DeprecatedOperationsClient::class + : OperationsClient::class; $operationsClientClass = $this->pluck('operationsClientClass', $options, false) - ?: OperationsCLient::class; + ?: $defaultClass; return new $operationsClientClass($options); } diff --git a/Gax/src/IamProviderInterface.php b/Gax/src/IamProviderInterface.php new file mode 100644 index 000000000000..92b41802ee31 --- /dev/null +++ b/Gax/src/IamProviderInterface.php @@ -0,0 +1,119 @@ +addMiddleware(function (MiddlewareInterface $handler) { + * return new class ($handler) implements MiddlewareInterface { + * public function __construct(private MiddlewareInterface $handler) { + * } + * + * public function __invoke(Call $call, array $options) { + * // modify call and options (pre-request) + * $response = ($this->handler)($call, $options); + * // modify the response (post-request) + * return $response; + * } + * }; + * }); + * ``` + * + * @param callable $middlewareCallable A callable which returns an instance + * of {@see \Google\ApiCore\Middleware\MiddlewareInterface} when invoked with a + * MiddlewareInterface instance as its first argument. + * @return void + */ + public function addMiddleware(callable $middlewareCallable): void; + + /** + * Prepend a middleware to the call stack by providing a callable which will be + * invoked at the end of each call, and will return an instance of + * {@see \Google\ApiCore\Middleware\MiddlewareInterface} when invoked. + * + * The callable must have the following method signature: + * + * callable(MiddlewareInterface): MiddlewareInterface + * + * An implementation may look something like this: + * ``` + * $client->prependMiddleware(function (MiddlewareInterface $handler) { + * return new class ($handler) implements MiddlewareInterface { + * public function __construct(private MiddlewareInterface $handler) { + * } + * + * public function __invoke(Call $call, array $options) { + * // modify call and options (pre-request) + * $response = ($this->handler)($call, $options); + * // modify the response (post-request) + * return $response; + * } + * }; + * }); + * ``` + * + * @param callable $middlewareCallable A callable which returns an instance + * of {@see \Google\ApiCore\Middleware\MiddlewareInterface} when invoked with a + * MiddlewareInterface instance as its first argument. + * @return void + */ + public function prependMiddleware(callable $middlewareCallable): void; + + /** + * Get the default scopes required by the service. + * + * @return array + */ + public static function getServiceScopes(): array; + + /** + * Initiates an orderly shutdown in which preexisting calls continue but new + * calls are immediately cancelled. + * + * @return void + */ + public function close(): void; +} diff --git a/Gax/tests/Unit/GapicClientTraitTest.php b/Gax/tests/Unit/GapicClientTraitTest.php index e6cec22e1a3b..e1115ad00ae4 100644 --- a/Gax/tests/Unit/GapicClientTraitTest.php +++ b/Gax/tests/Unit/GapicClientTraitTest.php @@ -39,12 +39,15 @@ use Google\ApiCore\ClientStream; use Google\ApiCore\CredentialsWrapper; use Google\ApiCore\GapicClientTrait; -use Google\ApiCore\LongRunning\OperationsClient; +use Google\ApiCore\IamProviderInterface; +use Google\ApiCore\LongRunning\OperationsClient as DeprecatedOperationsClient; +use Google\ApiCore\LongRunningOperationProviderInterface; use Google\ApiCore\Middleware\MiddlewareInterface; use Google\ApiCore\OperationResponse; use Google\ApiCore\RequestParamsHeaderDescriptor; use Google\ApiCore\RetrySettings; use Google\ApiCore\ServerStream; +use Google\ApiCore\ServiceInterface; use Google\ApiCore\Testing\MockRequest; use Google\ApiCore\Testing\MockRequestBody; use Google\ApiCore\Testing\MockResponse; @@ -54,6 +57,13 @@ use Google\ApiCore\Transport\TransportInterface; use Google\ApiCore\ValidationException; use Google\Auth\FetchAuthTokenInterface; +use Google\Cloud\Iam\V1\GetIamPolicyRequest; +use Google\Cloud\Iam\V1\Policy; +use Google\Cloud\Iam\V1\SetIamPolicyRequest; +use Google\Cloud\Iam\V1\TestIamPermissionsRequest; +use Google\Cloud\Iam\V1\TestIamPermissionsResponse; +use Google\LongRunning\Client\OperationsClient; +use Google\LongRunning\GetOperationRequest; use Google\LongRunning\Operation; use Grpc\Gcp\Config; use GuzzleHttp\Promise\FulfilledPromise; @@ -1968,6 +1978,113 @@ public function testGetServiceScopes() DefaultScopeAndAudienceGapicClient::getServiceScopes() ); } + + public function testCreateOperationsClientDefaultClass() + { + $v1Client = new StubGapicClient(); + $this->assertInstanceOf( + DeprecatedOperationsClient::class, + $v1Client->createOperationsClient([]) + ); + + $v2Client = new GapicV2SurfaceClient(); + $method = new \ReflectionMethod($v2Client, 'createOperationsClient'); + $this->assertInstanceOf( + OperationsClient::class, + $method->invoke($v2Client, []) + ); + } + + public function testServiceInterface() + { + $transport = $this->prophesize(TransportInterface::class); + $transport->close()->shouldBeCalledOnce(); + + $client = new GapicV2SurfaceClient([ + 'transport' => $transport->reveal(), + ]); + + $this->assertInstanceOf(ServiceInterface::class, $client); + $this->assertEquals([], GapicV2SurfaceClient::getServiceScopes()); + $client->close(); + } + + public function testLongRunningOperationProviderInterface() + { + $operationsClient = $this->prophesize(OperationsClient::class); + $operationsClient->getOperation( + Argument::that(fn (GetOperationRequest $req) => $req->getName() === 'operations/test-op') + ) + ->shouldBeCalledOnce() + ->willReturn(new Operation(['name' => 'operations/test-op', 'done' => true])); + + $client = new GapicV2SurfaceClient([ + 'operationsClient' => $operationsClient->reveal(), + ]); + + $this->assertInstanceOf(LongRunningOperationProviderInterface::class, $client); + $this->assertSame($operationsClient->reveal(), $client->getOperationsClient()); + + $operation = $client->resumeOperation('operations/test-op'); + $this->assertInstanceOf(OperationResponse::class, $operation); + $this->assertEquals('operations/test-op', $operation->getName()); + $this->assertTrue($operation->isDone()); + } + + public function testIamProviderInterface() + { + $policy = new Policy(); + $permissionsResponse = new TestIamPermissionsResponse(); + + $transport = $this->prophesize(TransportInterface::class); + $transport->startUnaryCall( + Argument::that(fn (Call $call) => $call->getMethod() === 'test.interface.v1.api/GetIamPolicy'), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new FulfilledPromise($policy)); + $transport->startUnaryCall( + Argument::that(fn (Call $call) => $call->getMethod() === 'test.interface.v1.api/SetIamPolicy'), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new FulfilledPromise($policy)); + $transport->startUnaryCall( + Argument::that(fn (Call $call) => $call->getMethod() === 'test.interface.v1.api/TestIamPermissions'), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new FulfilledPromise($permissionsResponse)); + + $client = new GapicV2SurfaceClient([ + 'transport' => $transport->reveal(), + ]); + $client->set('descriptors', [ + 'GetIamPolicy' => [ + 'callType' => Call::UNARY_CALL, + 'responseType' => Policy::class, + ], + 'SetIamPolicy' => [ + 'callType' => Call::UNARY_CALL, + 'responseType' => Policy::class, + ], + 'TestIamPermissions' => [ + 'callType' => Call::UNARY_CALL, + 'responseType' => TestIamPermissionsResponse::class, + ], + ]); + $retrySettings = RetrySettings::constructDefault(); + $client->set('retrySettings', [ + 'GetIamPolicy' => $retrySettings, + 'SetIamPolicy' => $retrySettings, + 'TestIamPermissions' => $retrySettings, + ]); + + $this->assertInstanceOf(IamProviderInterface::class, $client); + $this->assertSame($policy, $client->getIamPolicy(new GetIamPolicyRequest())); + $this->assertSame($policy, $client->setIamPolicy(new SetIamPolicyRequest())); + $this->assertSame($permissionsResponse, $client->testIamPermissions(new TestIamPermissionsRequest())); + } } class StubGapicClient @@ -2158,7 +2275,7 @@ public function getOperation($name, $arg1, $arg2) } } -class GapicV2SurfaceClient +class GapicV2SurfaceClient implements ServiceInterface, LongRunningOperationProviderInterface, IamProviderInterface { use GapicClientTrait { startCall as public; @@ -2168,10 +2285,14 @@ class GapicV2SurfaceClient ClientDefaultsTrait::getClientDefaults insteadof GapicClientTrait; } + public static array $serviceScopes = []; + private OperationsClient $operationsClient; + public function __construct(array $options = []) { $clientOptions = $this->buildClientOptions($options); $this->setClientOptions($clientOptions); + $this->operationsClient = $this->createOperationsClient($clientOptions); } public function getAgentHeader() @@ -2179,4 +2300,33 @@ public function getAgentHeader() return $this->agentHeader; } + public function getOperationsClient(): OperationsClient + { + return $this->operationsClient; + } + + public function resumeOperation(string $operationName, ?string $methodName = null): OperationResponse + { + $options = $this->descriptors[$methodName]['longRunning'] ?? []; + $operation = new OperationResponse($operationName, $this->getOperationsClient(), $options); + $operation->reload(); + return $operation; + } + + public function getIamPolicy(GetIamPolicyRequest $request, array $callOptions = []): Policy + { + return $this->startApiCall('GetIamPolicy', $request, $callOptions)->wait(); + } + + public function setIamPolicy(SetIamPolicyRequest $request, array $callOptions = []): Policy + { + return $this->startApiCall('SetIamPolicy', $request, $callOptions)->wait(); + } + + public function testIamPermissions( + TestIamPermissionsRequest $request, + array $callOptions = [] + ): TestIamPermissionsResponse { + return $this->startApiCall('TestIamPermissions', $request, $callOptions)->wait(); + } }