Skip to content

Commit

Permalink
feat: support V2 OperationsClient in OperationResponse (#564)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajupazhamayil authored May 10, 2024
1 parent 9b06449 commit 7f8bb13
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 30 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"guzzlehttp/promises": "^2.0",
"guzzlehttp/psr7": "^2.0",
"google/common-protos": "^4.4",
"google/longrunning": "~0.2",
"google/longrunning": "~0.4",
"ramsey/uuid": "^4.0"
},
"require-dev": {
Expand Down
24 changes: 18 additions & 6 deletions src/OperationResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@

namespace Google\ApiCore;

use Google\LongRunning\Client\OperationsClient;
use Google\LongRunning\CancelOperationRequest;
use Google\LongRunning\DeleteOperationRequest;
use Google\LongRunning\GetOperationRequest;
use Google\LongRunning\Operation;
use Google\Protobuf\Any;
use Google\Protobuf\Internal\Message;
Expand Down Expand Up @@ -261,8 +265,7 @@ public function reload()
}
$this->lastProtoResponse = $this->operationsCall(
$this->getOperationMethod,
$this->getName(),
$this->additionalArgs
GetOperationRequest::class
);
}

Expand Down Expand Up @@ -386,7 +389,10 @@ public function cancel()
if (is_null($this->cancelOperationMethod)) {
throw new LogicException('The cancel operation is not supported by this API');
}
$this->operationsCall($this->cancelOperationMethod, $this->getName(), $this->additionalArgs);
$this->operationsCall(
$this->cancelOperationMethod,
CancelOperationRequest::class
);
}

/**
Expand All @@ -405,7 +411,10 @@ public function delete()
if (is_null($this->deleteOperationMethod)) {
throw new LogicException('The delete operation is not supported by this API');
}
$this->operationsCall($this->deleteOperationMethod, $this->getName(), $this->additionalArgs);
$this->operationsCall(
$this->deleteOperationMethod,
DeleteOperationRequest::class
);
$this->deleted = true;
}

Expand Down Expand Up @@ -447,9 +456,12 @@ public function getMetadata()
return $metadata;
}

private function operationsCall($method, $name, array $additionalArgs)
private function operationsCall(string $method, string $requestClass)
{
$args = array_merge([$name], $additionalArgs);
$firstArgument = $this->operationsClient instanceof OperationsClient
? $requestClass::build($this->getName())
: $this->getName();
$args = array_merge([$firstArgument], $this->additionalArgs);
return call_user_func_array([$this->operationsClient, $method], $args);
}

Expand Down
111 changes: 88 additions & 23 deletions tests/Tests/Unit/OperationResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,48 @@
use Google\ApiCore\LongRunning\OperationsClient;
use Google\ApiCore\OperationResponse;
use Google\LongRunning\Operation;
use Google\LongRunning\Client\OperationsClient as LROOperationsClient;
use Google\LongRunning\CancelOperationRequest;
use Google\LongRunning\DeleteOperationRequest;
use Google\LongRunning\GetOperationRequest;
use Google\Protobuf\Any;
use Google\Rpc\Code;
use LogicException;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;

class OperationResponseTest extends TestCase
{
use ProphecyTrait;
use TestTrait;

public function testBasic()
/**
* @dataProvider provideOperationsClients
*/
public function testBasic($opClient)
{
$opName = 'operations/opname';
$opClient = $this->createOperationsClient();
$op = new OperationResponse($opName, $opClient);

$this->assertSame($opName, $op->getName());
$this->assertSame($opClient, $op->getOperationsClient());
}

public function testWithoutResponse()
public function provideOperationsClients()
{
return [
[$this->createOperationsClient()],
[$this->prophesize(LROOperationsClient::class)->reveal()],
];
}

/**
* @dataProvider provideOperationsClients
*/
public function testWithoutResponse($opClient)
{
$opName = 'operations/opname';
$opClient = $this->createOperationsClient();
$op = new OperationResponse($opName, $opClient);

$this->assertNull($op->getLastProtoResponse());
Expand All @@ -78,10 +95,12 @@ public function testWithoutResponse()
], $op->getDescriptorOptions());
}

public function testWithResponse()
/**
* @dataProvider provideOperationsClients
*/
public function testWithResponse($opClient)
{
$opName = 'operations/opname';
$opClient = $this->createOperationsClient();
$protoResponse = new Operation();
$op = new OperationResponse($opName, $opClient, [
'lastProtoResponse' => $protoResponse,
Expand Down Expand Up @@ -123,10 +142,12 @@ public function testWithResponse()
$this->assertTrue($op->operationFailed());
}

public function testWithOptions()
/**
* @dataProvider provideOperationsClients
*/
public function testWithOptions($opClient)
{
$opName = 'operations/opname';
$opClient = $this->createOperationsClient();
$protoResponse = new Operation();
$op = new OperationResponse($opName, $opClient, [
'operationReturnType' => '\Google\Rpc\Status',
Expand Down Expand Up @@ -238,7 +259,10 @@ public function testCustomOperation()
$operationResponse->delete();
}

public function testCustomOperationError()
/**
* @dataProvider provideOperationsClients
*/
public function testCustomOperationError($operationClient)
{
$operationName = 'test-123';
$operation = $this->prophesize(CustomOperationWithErrorAnnotations::class);
Expand All @@ -251,15 +275,14 @@ public function testCustomOperationError()
$operation->getTheErrorMessage()
->shouldBeCalledOnce()
->willReturn('It failed, sorry :(');
$operationClient = $this->prophesize(CustomOperationClient::class);
$options = [
'operationStatusMethod' => 'isThisOperationDoneOrWhat',
'operationStatusDoneValue' => 'Yes, it is!',
'operationErrorCodeMethod' => 'getTheErrorCode',
'operationErrorMessageMethod' => 'getTheErrorMessage',
'lastProtoResponse' => $operation->reveal(),
];
$operationResponse = new OperationResponse($operationName, $operationClient->reveal(), $options);
$operationResponse = new OperationResponse($operationName, $operationClient, $options);

$this->assertFalse($operationResponse->operationSucceeded());

Expand All @@ -270,7 +293,10 @@ public function testCustomOperationError()
$this->assertSame('It failed, sorry :(', $error->getMessage());
}

public function testEmptyCustomOperationErrorIsSuccessful()
/**
* @dataProvider provideOperationsClients
*/
public function testEmptyCustomOperationErrorIsSuccessful($operationClient)
{
$operationName = 'test-123';
$operation = $this->prophesize(CustomOperationWithErrorAnnotations::class);
Expand All @@ -280,61 +306,66 @@ public function testEmptyCustomOperationErrorIsSuccessful()
$operation->getTheErrorCode()
->shouldBeCalledOnce()
->willReturn(null);
$operationClient = $this->prophesize(CustomOperationClient::class);
$options = [
'operationStatusMethod' => 'isThisOperationDoneOrWhat',
'operationStatusDoneValue' => 'Yes, it is!',
'operationErrorCodeMethod' => 'getTheErrorCode',
'lastProtoResponse' => $operation->reveal(),
];
$operationResponse = new OperationResponse($operationName, $operationClient->reveal(), $options);
$operationResponse = new OperationResponse($operationName, $operationClient, $options);

$this->assertTrue($operationResponse->operationSucceeded());
}

public function testMisconfiguredCustomOperationThrowsException()
/**
* @dataProvider provideOperationsClients
*/
public function testMisconfiguredCustomOperationThrowsException($operationClient)
{
$operationName = 'test-123';
$operation = $this->prophesize(CustomOperationWithErrorAnnotations::class);
$operation->isThisOperationDoneOrWhat()
->shouldBeCalledOnce()
->willReturn('Yes, it is!');
$operationClient = $this->prophesize(CustomOperationClient::class);
$options = [
'operationStatusMethod' => 'isThisOperationDoneOrWhat',
'operationStatusDoneValue' => 'Yes, it is!',
'operationErrorCodeMethod' => null, // The OperationResponse has no way to determine error status
'lastProtoResponse' => $operation->reveal(),
];
$operationResponse = new OperationResponse($operationName, $operationClient->reveal(), $options);
$operationResponse = new OperationResponse($operationName, $operationClient, $options);

$this->expectException(LogicException::class);
$this->expectExceptionMessage('Unable to determine operation error status for this service');

$operationResponse->operationSucceeded();
}

public function testNoCancelOperation()
/**
* @dataProvider provideOperationsClients
*/
public function testNoCancelOperation($operationClient)
{
$operationClient = $this->prophesize(CustomOperationClient::class);
$options = [
'cancelOperationMethod' => null,
];
$operationResponse = new OperationResponse('test-123', $operationClient->reveal(), $options);
$operationResponse = new OperationResponse('test-123', $operationClient, $options);

$this->expectException(LogicException::class);
$this->expectExceptionMessage('The cancel operation is not supported by this API');

$operationResponse->cancel();
}

public function testNoDeleteOperation()
/**
* @dataProvider provideOperationsClients
*/
public function testNoDeleteOperation($operationClient)
{
$operationClient = $this->prophesize(CustomOperationClient::class);
$options = [
'deleteOperationMethod' => null,
];
$operationResponse = new OperationResponse('test-123', $operationClient->reveal(), $options);
$operationResponse = new OperationResponse('test-123', $operationClient, $options);

$this->expectException(LogicException::class);
$this->expectExceptionMessage('The delete operation is not supported by this API');
Expand All @@ -353,6 +384,40 @@ public function testPollingCastToInt()
$this->assertEquals($op->getSleeps(), [3, 4, 6]);
}

public function testReloadWithLROOperationsClient()
{
$operationClient = $this->prophesize(LROOperationsClient::class);
$request = new GetOperationRequest(['name' => 'test-123']);
$operationClient->getOperation($request)
->shouldBeCalledOnce()
->willReturn(new Operation());

$operationResponse = new OperationResponse('test-123', $operationClient->reveal());
$operationResponse->reload();
}

public function testCancelWithLROOperationsClient()
{
$operationClient = $this->prophesize(LROOperationsClient::class);
$request = new CancelOperationRequest(['name' => 'test-123']);
$operationClient->cancelOperation($request)
->shouldBeCalledOnce();

$operationResponse = new OperationResponse('test-123', $operationClient->reveal());
$operationResponse->cancel();
}

public function testDeleteWithLROOperationsClient()
{
$operationClient = $this->prophesize(LROOperationsClient::class);
$request = new DeleteOperationRequest(['name' => 'test-123']);
$operationClient->deleteOperation($request)
->shouldBeCalledOnce();

$operationResponse = new OperationResponse('test-123', $operationClient->reveal());
$operationResponse->delete();
}

private function createOperationResponse($options, $reloadCount)
{
$opName = 'operations/opname';
Expand Down

0 comments on commit 7f8bb13

Please sign in to comment.