Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: [Spanner] Upgrade to V2 (Part 1/2) #7193

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0f9b3b7
feat!: [Spanner] Upgrade to PHP V2
ajupazhamayil Apr 1, 2024
37076aa
add RequestHandler to SpannerClient
ajupazhamayil Apr 1, 2024
f9fb788
make LRO work for create database operation
ajupazhamayil Apr 18, 2024
6ae9e61
Replace Connection class in Database.php
ajupazhamayil May 7, 2024
4bed2dd
Replace Conncection class objects in Operation and Session classess
ajupazhamayil May 8, 2024
996fecf
Resolve unittest issues in TransactionTypeTest and TransactionTest
ajupazhamayil May 14, 2024
dd44928
Resolve System/AdminTest issues
ajupazhamayil May 15, 2024
dd83885
Resolve System test issues: BatchTest
ajupazhamayil May 15, 2024
0966a99
Resolve system/TransactionTest issues
ajupazhamayil May 15, 2024
5b9841d
resolve system tests issues of CacheSessionPool
ajupazhamayil May 15, 2024
c555140
make requestHandler return Promise
ajupazhamayil May 15, 2024
143e700
Resolve styling issues
ajupazhamayil May 15, 2024
677d853
Resolve PG tests
ajupazhamayil May 16, 2024
1bb522e
rename LROManagerTrait to LongRunningOperationTrait
ajupazhamayil May 19, 2024
560d12e
Remove extra decodeMessages
ajupazhamayil May 21, 2024
3da94d1
Resolve system tests issues
ajupazhamayil May 21, 2024
aca7c7a
Improve code readablity
ajupazhamayil May 22, 2024
d254501
Resolve unit test issues
ajupazhamayil May 23, 2024
5b440b4
remove scopes to see the unit tests failures
ajupazhamayil May 23, 2024
0fcd821
rollback the changes for testing
ajupazhamayil May 23, 2024
a743df5
Fix tests/Unit/DatabaseTest.php
ajupazhamayil May 24, 2024
6ddc272
Remove projectId from the DatabaseTest
ajupazhamayil May 24, 2024
abf2102
Resolve unittest ADC issues
ajupazhamayil May 24, 2024
8dd9cfe
make the unittests pass
ajupazhamayil May 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
426 changes: 426 additions & 0 deletions Core/src/LongRunning/LongRunningOperationManager.php

Large diffs are not rendered by default.

159 changes: 159 additions & 0 deletions Core/src/LongRunning/LongRunningOperationTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php
/**
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Core\LongRunning;

use Google\ApiCore\Serializer;
use Google\Cloud\Core\Iterator\ItemIterator;
use Google\Cloud\Core\Iterator\PageIterator;
use Google\Cloud\Core\RequestHandler;
use Google\LongRunning\ListOperationsRequest;

/**
* Provide Long Running Operation support to Google Cloud PHP Clients.
*
* This trait should be used by a user-facing client which implements LRO.
*/
trait LongRunningOperationTrait
{
/**
* @var string
*/
private $lroResource;

/**
* @var string
*/
private $clientClass;

/**
* @var array
*/
private $lroResponseMapper;

/**
* Populate required LRO properties.
*
* @param RequestHandler The request handler that is responsible for sending a request
* and serializing responses into relevant classes.
* @param Serializer $serializer The serializer instance to encode/decode messages.
* @param array $callablesMap An collection of form [(string) typeUrl, (callable) callable]
* providing a function to invoke when an operation completes. The
* callable Type should correspond to an expected value of
* operation.metadata.typeUrl.
* @param array $lroResponseMappers A list of mappers for deserializing operation results.
* @param string $lroResource [optional] The resource for which operations
* may be listed.
* @param string $clientClass [Optional] The request will be forwarded to this client class.
*/
private function setLroProperties(
RequestHandler $requestHandler,
Serializer $serializer,
array $lroCallables,
array $lroResponseMapper,
string $lroResource = null,
string $clientClass = null
) {
$this->requestHandler = $requestHandler;
$this->serializer = $serializer;
$this->lroCallables = $lroCallables;
$this->lroResponseMapper = $lroResponseMapper;
$this->lroResource = $lroResource;
$this->clientClass = $clientClass;
}

/**
* Resume a Long Running Operation
*
* @param string $operationName The Long Running Operation name.
* @param array $lroResponseMappers A list of mappers for deserializing operation results.
* @param array $info [optional] The operation data.
* @return LongRunningOperationManger
*/
public function resumeOperation($operationName, array $info = [])
{
return new LongRunningOperationManager(
$this->requestHandler,
$this->serializer,
$this->lroCallables,
$this->lroResponseMapper,
$this->clientClass,
$operationName,
$info
);
}

/**
* List long running operations.
*
* @param array $options [optional] {
* Configuration Options.
*
* @type string $name The name of the operation collection.
* @type string $filter The standard list filter.
* @type int $pageSize Maximum number of results to return per
* request.
* @type int $resultLimit Limit the number of results returned in total.
* **Defaults to** `0` (return all results).
* @type string $pageToken A previously-returned page token used to
* resume the loading of results from a specific point.
* }
* @return ItemIterator<LongRunningOperation>
*/
public function longRunningOperations(array $options = [])
{
if (is_null($this->lroResource) || is_null($this->clientClass)) {
throw new \BadMethodCallException('This service does not support listing operations.');
}
list($data, $optionalArgs) = $this->splitOptionalArgs($options);
$resultLimit = $this->pluck('resultLimit', $data, false) ?: 0;
$data['name'] = $this->lroResource .'/operations';

$client = $this->requestHandler->getClientObject($this->clientClass);
$operationsClient = $client->getOperationsClient();
if (is_null($operationsClient)) {
throw new \BadMethodCallException('This service does not support listing operations.');
}
$this->requestHandler->addClientObject(get_class($operationsClient), $operationsClient);
$request = $this->serializer->decodeMessage(new ListOperationsRequest(), $data);

return new ItemIterator(
new PageIterator(
function (array $operation) {
return $this->resumeOperation($operation['name'], $operation);
},
function ($callOptions) use ($optionalArgs, $request, $operationsClient) {
if (isset($callOptions['pageToken'])) {
$request->setPageToken($callOptions['pageToken']);
}

return $this->requestHandler->sendRequest(
get_class($operationsClient),
'listOperations',
$request,
$optionalArgs
);
},
$options,
[
'itemsKey' => 'operations',
'resultLimit' => $resultLimit
]
)
);
}
}
15 changes: 13 additions & 2 deletions Core/src/RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function __construct(
// Initialize the client classes and store them in memory
$this->clients = [];
foreach ($clientClasses as $className) {
$this->clients[$className] = new $className($clientConfig);
$this->addClientObject($className, new $className($clientConfig));
}
}

Expand Down Expand Up @@ -135,8 +135,19 @@ public function sendRequest(
* @param $clientClass The client class whose object we need.
* @return mixed
*/
private function getClientObject(string $clientClass)
public function getClientObject(string $clientClass)
{
return $this->clients[$clientClass] ?? null;
}

/**
* Helper function to add a client object to the $clients array.
*
* @param $clientName The class name of the client.
* @param $clientObject The client object to be added.
*/
public function addClientObject(string $clientName, mixed $clientObject)
{
$this->clients[$clientName] = $clientObject;
}
}
5 changes: 3 additions & 2 deletions Core/src/RequestProcessorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use \Google\Protobuf\Internal\Message;
use Google\Rpc\RetryInfo;
use Google\Rpc\BadRequest;
use GuzzleHttp\Promise\PromiseInterface;

/**
* @internal
Expand All @@ -45,7 +46,7 @@ trait RequestProcessorTrait
* Serializes a gRPC response.
*
* @param mixed $response
* @return \Generator|OperationResponse|array|null
* @return \Generator|OperationResponse|array|PromiseInterface|null
*/
private function handleResponse($response)
{
Expand All @@ -57,7 +58,7 @@ private function handleResponse($response)
return $this->serializer->encodeMessage($response);
}

if ($response instanceof OperationResponse) {
if ($response instanceof OperationResponse || $response instanceof PromiseInterface) {
return $response;
}

Expand Down
5 changes: 0 additions & 5 deletions Core/tests/Unit/ServiceBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,6 @@ public function serviceProvider()
], [
'language',
LanguageClient::class
], [
'spanner',
SpannerClient::class,
[],
[$this, 'checkAndSkipGrpcTests']
], [
'speech',
SpeechClient::class,
Expand Down
11 changes: 9 additions & 2 deletions Spanner/src/Admin/Database/V1/Client/DatabaseAdminClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
use Google\ApiCore\CredentialsWrapper;
use Google\ApiCore\GapicClientTrait;
use Google\ApiCore\InsecureCredentialsWrapper;
use Google\ApiCore\LongRunning\OperationsClient;
use Google\ApiCore\OperationResponse;
use Google\ApiCore\PagedListResponse;
use Google\ApiCore\ResourceHelperTrait;
Expand Down Expand Up @@ -67,6 +66,7 @@
use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest;
use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseMetadata;
use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest;
use Google\LongRunning\Client\OperationsClient;
use Google\LongRunning\Operation;
use Grpc\ChannelCredentials;
use GuzzleHttp\Promise\PromiseInterface;
Expand Down Expand Up @@ -184,6 +184,9 @@ public function getOperationsClient()
public function resumeOperation($operationName, $methodName = null)
{
$options = isset($this->descriptors[$methodName]['longRunning']) ? $this->descriptors[$methodName]['longRunning'] : [];
// OperationResponse is a V1 surface class, we need V2 surface class.
// [V2 LongRunning/Client/OperationsClient, getOperation]
// Other clients have also used the OperationResponse similarly.
$operation = new OperationResponse($operationName, $this->getOperationsClient(), $options);
$operation->reload();
return $operation;
Expand Down Expand Up @@ -379,7 +382,11 @@ public function __construct(array $options = [])
$options = $options + $this->getDefaultEmulatorConfig();
$clientOptions = $this->buildClientOptions($options);
$this->setClientOptions($clientOptions);
$this->operationsClient = $this->createOperationsClient($clientOptions);
// TODO: [Remove this] Added this here for demonstrating the
// generated code changes for V2 OperationsClient usage.
$this->operationsClient = $this->createOperationsClient(
$clientOptions + ['operationsClientClass' => OperationsClient::class]
);
}

/** Handles execution of the async variants for each documented method. */
Expand Down
85 changes: 4 additions & 81 deletions Spanner/src/Connection/Grpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceMetadata;
use Google\Cloud\Spanner\Operation;
use Google\Cloud\Spanner\SpannerClient as ManualSpannerClient;
use Google\Cloud\Spanner\RequestHeaderTrait;
use Google\Cloud\Spanner\RequestTrait;
use Google\Cloud\Spanner\V1\CreateSessionRequest;
use Google\Cloud\Spanner\V1\DeleteSessionRequest;
use Google\Cloud\Spanner\V1\DirectedReadOptions;
Expand Down Expand Up @@ -84,7 +84,7 @@ class Grpc implements ConnectionInterface
use EmulatorTrait;
use GrpcTrait;
use OperationResponseTrait;
use RequestHeaderTrait;
use RequestTrait;

/**
* @var InstanceAdminClient|null
Expand Down Expand Up @@ -127,85 +127,6 @@ class Grpc implements ConnectionInterface
'delete' => 'setDelete'
];

/**
* @var array
*/
private $lroResponseMappers = [
[
'method' => 'updateDatabaseDdl',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata',
'message' => UpdateDatabaseDdlMetadata::class
], [
'method' => 'createDatabase',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CreateDatabaseMetadata',
'message' => CreateDatabaseMetadata::class
], [
'method' => 'createInstanceConfig',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceConfigMetadata',
'message' => CreateInstanceConfigMetadata::class
], [
'method' => 'updateInstanceConfig',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata',
'message' => UpdateInstanceConfigMetadata::class
], [
'method' => 'createInstance',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceMetadata',
'message' => CreateInstanceMetadata::class
], [
'method' => 'updateInstance',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.UpdateInstanceMetadata',
'message' => UpdateInstanceMetadata::class
], [
'method' => 'createBackup',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata',
'message' => CreateBackupMetadata::class
], [
'method' => 'copyBackup',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CopyBackupMetadata',
'message' => CopyBackupMetadata::class
], [
'method' => 'restoreDatabase',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata',
'message' => RestoreDatabaseMetadata::class
], [
'method' => 'restoreDatabase',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata',
'message' => OptimizeRestoredDatabaseMetadata::class
], [
'method' => 'updateDatabaseDdl',
'typeUrl' => 'type.googleapis.com/google.protobuf.Empty',
'message' => GPBEmpty::class
], [
'method' => 'createDatabase',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.Database',
'message' => Database::class
], [
'method' => 'createInstanceConfig',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.InstanceConfig',
'message' => InstanceConfig::class
], [
'method' => 'updateInstanceConfig',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.InstanceConfig',
'message' => InstanceConfig::class
], [
'method' => 'createInstance',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.Instance',
'message' => Instance::class
], [
'method' => 'updateInstance',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.Instance',
'message' => Instance::class
], [
'method' => 'createBackup',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.Backup',
'message' => Backup::class
], [
'method' => 'restoreDatabase',
'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.Database',
'message' => Database::class
]
];

/**
* @var CredentialsWrapper
*/
Expand All @@ -215,6 +136,7 @@ class Grpc implements ConnectionInterface
* @var bool
*/
private $larEnabled;
private $lroResponseMappers;

/**
* @param array $config [optional]
Expand Down Expand Up @@ -278,6 +200,7 @@ public function __construct(array $config = [])

$this->grpcConfig = $grpcConfig;
$this->larEnabled = $this->pluck('routeToLeader', $config, false) ?? true;
$this->lroResponseMappers = $this->getLROResponseMappers();
}

/**
Expand Down