From 1f6b5e40696f26471232259a9de34c16b44e1eed Mon Sep 17 00:00:00 2001 From: Brent Shaffer Date: Mon, 11 Nov 2024 20:54:53 -0800 Subject: [PATCH 1/8] feat!: Spanner V2 --- Core/src/ApiHelperTrait.php | 4 +- Core/src/Iam/Iam.php | 2 +- Core/src/RequestHandler.php | 17 +- Core/src/RequestProcessorTrait.php | 5 +- Core/src/ServiceBuilder.php | 2 +- Core/tests/Snippet/Iam/IamManagerTest.php | 2 +- Core/tests/Snippet/Iam/IamTest.php | 3 +- .../LongRunningOperationManagerTest.php | 420 ++++ .../OperationResponseTraitTest.php | 7 +- Core/tests/Unit/ServiceBuilderTest.php | 6 - Spanner/MIGRATING.md | 115 + Spanner/composer.json | 19 +- Spanner/owlbot.py | 49 +- Spanner/phpunit.xml.dist | 2 +- Spanner/src/Admin/Database/V1/Backup.php | 18 +- Spanner/src/Admin/Database/V1/BackupInfo.php | 8 +- .../src/Admin/Database/V1/Backup_State.php | 16 - .../V1/Client/DatabaseAdminClient.php | 23 +- .../V1/CopyBackupEncryptionConfig.php | 4 +- ...yBackupEncryptionConfig_EncryptionType.php | 16 - .../Admin/Database/V1/CopyBackupMetadata.php | 8 +- .../Admin/Database/V1/CopyBackupRequest.php | 10 +- .../V1/CreateBackupEncryptionConfig.php | 4 +- ...eBackupEncryptionConfig_EncryptionType.php | 16 - .../Database/V1/CreateBackupMetadata.php | 8 +- .../Admin/Database/V1/CreateBackupRequest.php | 8 +- .../Database/V1/CreateDatabaseMetadata.php | 2 +- .../Database/V1/CreateDatabaseRequest.php | 10 +- Spanner/src/Admin/Database/V1/Database.php | 22 +- .../Database/V1/DatabaseAdminGrpcClient.php | 473 ---- .../src/Admin/Database/V1/DatabaseRole.php | 2 +- .../src/Admin/Database/V1/Database_State.php | 16 - .../Database/V1/DdlStatementActionInfo.php | 4 +- .../Admin/Database/V1/DeleteBackupRequest.php | 2 +- .../Admin/Database/V1/DropDatabaseRequest.php | 2 +- .../Admin/Database/V1/EncryptionConfig.php | 2 +- .../src/Admin/Database/V1/EncryptionInfo.php | 6 +- .../Admin/Database/V1/EncryptionInfo_Type.php | 16 - .../Admin/Database/V1/GetBackupRequest.php | 2 +- .../Database/V1/GetDatabaseDdlRequest.php | 2 +- .../Database/V1/GetDatabaseDdlResponse.php | 2 +- .../Admin/Database/V1/GetDatabaseRequest.php | 2 +- .../V1/ListBackupOperationsRequest.php | 8 +- .../V1/ListBackupOperationsResponse.php | 2 +- .../Admin/Database/V1/ListBackupsRequest.php | 8 +- .../Admin/Database/V1/ListBackupsResponse.php | 2 +- .../V1/ListDatabaseOperationsRequest.php | 8 +- .../V1/ListDatabaseOperationsResponse.php | 2 +- .../Database/V1/ListDatabaseRolesRequest.php | 6 +- .../Database/V1/ListDatabaseRolesResponse.php | 2 +- .../Database/V1/ListDatabasesRequest.php | 6 +- .../Database/V1/ListDatabasesResponse.php | 2 +- .../Admin/Database/V1/OperationProgress.php | 6 +- .../V1/OptimizeRestoredDatabaseMetadata.php | 4 +- .../V1/RestoreDatabaseEncryptionConfig.php | 4 +- ...atabaseEncryptionConfig_EncryptionType.php | 16 - .../Database/V1/RestoreDatabaseMetadata.php | 10 +- .../Database/V1/RestoreDatabaseRequest.php | 6 +- Spanner/src/Admin/Database/V1/RestoreInfo.php | 2 +- .../Admin/Database/V1/UpdateBackupRequest.php | 4 +- .../Database/V1/UpdateDatabaseDdlMetadata.php | 4 +- .../Database/V1/UpdateDatabaseDdlRequest.php | 6 +- .../Database/V1/UpdateDatabaseMetadata.php | 6 +- .../Database/V1/UpdateDatabaseRequest.php | 4 +- .../Admin/Instance/V1/AutoscalingConfig.php | 2 +- .../AutoscalingConfig/AutoscalingTargets.php | 4 +- .../V1/Client/InstanceAdminClient.php | 23 +- .../V1/CreateInstanceConfigMetadata.php | 6 +- .../V1/CreateInstanceConfigRequest.php | 8 +- .../Instance/V1/CreateInstanceMetadata.php | 10 +- .../V1/CreateInstancePartitionMetadata.php | 8 +- .../V1/CreateInstancePartitionRequest.php | 6 +- .../Instance/V1/CreateInstanceRequest.php | 6 +- .../V1/DeleteInstanceConfigRequest.php | 6 +- .../V1/DeleteInstancePartitionRequest.php | 4 +- .../Instance/V1/DeleteInstanceRequest.php | 2 +- .../Instance/V1/GetInstanceConfigRequest.php | 2 +- .../V1/GetInstancePartitionRequest.php | 2 +- .../Admin/Instance/V1/GetInstanceRequest.php | 4 +- Spanner/src/Admin/Instance/V1/Instance.php | 16 +- .../Instance/V1/InstanceAdminGrpcClient.php | 460 ---- .../src/Admin/Instance/V1/InstanceConfig.php | 14 +- .../Instance/V1/InstanceConfig_State.php | 16 - .../Admin/Instance/V1/InstanceConfig_Type.php | 16 - .../Admin/Instance/V1/InstancePartition.php | 14 +- .../src/Admin/Instance/V1/Instance_State.php | 16 - .../ListInstanceConfigOperationsRequest.php | 8 +- .../ListInstanceConfigOperationsResponse.php | 2 +- .../V1/ListInstanceConfigsRequest.php | 6 +- .../V1/ListInstanceConfigsResponse.php | 2 +- ...ListInstancePartitionOperationsRequest.php | 10 +- ...istInstancePartitionOperationsResponse.php | 2 +- .../V1/ListInstancePartitionsRequest.php | 8 +- .../V1/ListInstancePartitionsResponse.php | 2 +- .../Instance/V1/ListInstancesRequest.php | 10 +- .../Instance/V1/ListInstancesResponse.php | 2 +- .../Admin/Instance/V1/OperationProgress.php | 6 +- Spanner/src/Admin/Instance/V1/ReplicaInfo.php | 6 +- .../Instance/V1/ReplicaInfo_ReplicaType.php | 16 - .../V1/UpdateInstanceConfigMetadata.php | 6 +- .../V1/UpdateInstanceConfigRequest.php | 6 +- .../Instance/V1/UpdateInstanceMetadata.php | 10 +- .../V1/UpdateInstancePartitionMetadata.php | 8 +- .../V1/UpdateInstancePartitionRequest.php | 4 +- .../Instance/V1/UpdateInstanceRequest.php | 4 +- Spanner/src/ArrayType.php | 2 +- Spanner/src/Backup.php | 257 ++- Spanner/src/Batch/BatchClient.php | 4 +- Spanner/src/Batch/BatchSnapshot.php | 3 +- Spanner/src/Batch/QueryPartition.php | 2 +- Spanner/src/Batch/ReadPartition.php | 2 +- Spanner/src/BatchDmlResult.php | 2 +- Spanner/src/Bytes.php | 2 +- Spanner/src/CommitTimestamp.php | 2 +- .../src/Connection/ConnectionInterface.php | 291 --- Spanner/src/Connection/Grpc.php | 1775 -------------- Spanner/src/Connection/IamDatabase.php | 65 - Spanner/src/Connection/IamInstance.php | 65 - .../src/Connection/LongRunningConnection.php | 75 - Spanner/src/Database.php | 703 ++++-- Spanner/src/Date.php | 2 +- Spanner/src/Duration.php | 121 - Spanner/src/Instance.php | 451 ++-- Spanner/src/InstanceConfiguration.php | 264 ++- Spanner/src/KeyRange.php | 2 +- Spanner/src/KeySet.php | 2 +- Spanner/src/MutationTrait.php | 4 +- Spanner/src/Numeric.php | 2 +- Spanner/src/Operation.php | 528 ++++- Spanner/src/PgJsonb.php | 2 +- Spanner/src/PgNumeric.php | 2 +- Spanner/src/PgOid.php | 2 +- ...equestHeaderTrait.php => RequestTrait.php} | 44 +- Spanner/src/Result.php | 15 +- Spanner/src/Session/CacheSessionPool.php | 15 +- Spanner/src/Session/Session.php | 105 +- Spanner/src/Snapshot.php | 2 +- Spanner/src/SnapshotTrait.php | 8 +- Spanner/src/SpannerClient.php | 328 +-- Spanner/src/StructType.php | 2 +- Spanner/src/StructValue.php | 2 +- Spanner/src/Timestamp.php | 2 +- Spanner/src/Transaction.php | 35 +- Spanner/src/TransactionConfigurationTrait.php | 22 +- Spanner/src/TransactionalReadTrait.php | 26 +- Spanner/src/V1/BatchCreateSessionsRequest.php | 6 +- Spanner/src/V1/BatchWriteRequest.php | 6 +- .../V1/BatchWriteRequest/MutationGroup.php | 2 - Spanner/src/V1/BatchWriteResponse.php | 4 +- Spanner/src/V1/BeginTransactionRequest.php | 4 +- Spanner/src/V1/Client/SpannerClient.php | 3 +- Spanner/src/V1/CommitResponse/CommitStats.php | 4 +- Spanner/src/V1/CommitResponse_CommitStats.php | 16 - Spanner/src/V1/CreateSessionRequest.php | 4 +- Spanner/src/V1/DeleteSessionRequest.php | 2 +- .../DirectedReadOptions/ExcludeReplicas.php | 2 - .../DirectedReadOptions/IncludeReplicas.php | 4 +- .../DirectedReadOptions/ReplicaSelection.php | 6 +- .../ReplicaSelection/Type.php | 2 - Spanner/src/V1/ExecuteBatchDmlRequest.php | 8 +- .../V1/ExecuteBatchDmlRequest/Statement.php | 6 +- .../V1/ExecuteBatchDmlRequest_Statement.php | 16 - Spanner/src/V1/ExecuteSqlRequest.php | 24 +- .../src/V1/ExecuteSqlRequest/QueryMode.php | 2 - .../src/V1/ExecuteSqlRequest/QueryOptions.php | 6 +- .../src/V1/ExecuteSqlRequest_QueryMode.php | 16 - .../src/V1/ExecuteSqlRequest_QueryOptions.php | 16 - Spanner/src/V1/GetSessionRequest.php | 2 +- Spanner/src/V1/KeySet.php | 2 +- Spanner/src/V1/ListSessionsRequest.php | 8 +- Spanner/src/V1/ListSessionsResponse.php | 2 +- Spanner/src/V1/Mutation/Delete.php | 6 +- Spanner/src/V1/Mutation/Write.php | 4 +- Spanner/src/V1/Mutation_Delete.php | 16 - Spanner/src/V1/Mutation_Write.php | 16 - Spanner/src/V1/Partition.php | 2 +- Spanner/src/V1/PartitionOptions.php | 4 +- Spanner/src/V1/PartitionQueryRequest.php | 10 +- Spanner/src/V1/PartitionReadRequest.php | 12 +- Spanner/src/V1/PartitionResponse.php | 2 +- Spanner/src/V1/PlanNode.php | 12 +- Spanner/src/V1/PlanNode/ChildLink.php | 8 +- Spanner/src/V1/PlanNode/Kind.php | 2 - .../src/V1/PlanNode/ShortRepresentation.php | 4 +- Spanner/src/V1/PlanNode_ChildLink.php | 16 - Spanner/src/V1/PlanNode_Kind.php | 16 - .../src/V1/PlanNode_ShortRepresentation.php | 16 - Spanner/src/V1/README.md | 2 +- Spanner/src/V1/ReadRequest.php | 26 +- Spanner/src/V1/ReadRequest/LockHint.php | 2 - Spanner/src/V1/ReadRequest/OrderBy.php | 2 - Spanner/src/V1/RequestOptions.php | 6 +- Spanner/src/V1/RequestOptions/Priority.php | 2 - Spanner/src/V1/RequestOptions_Priority.php | 16 - Spanner/src/V1/ResultSetMetadata.php | 6 +- Spanner/src/V1/ResultSetStats.php | 4 +- Spanner/src/V1/RollbackRequest.php | 4 +- Spanner/src/V1/Session.php | 10 +- Spanner/src/V1/SpannerGrpcClient.php | 373 --- Spanner/src/V1/StructType/Field.php | 6 +- Spanner/src/V1/StructType_Field.php | 16 - Spanner/src/V1/TransactionOptions.php | 2 +- .../src/V1/TransactionOptions/PBReadOnly.php | 6 +- .../V1/TransactionOptions/PartitionedDml.php | 2 - .../ReadWrite/ReadLockMode.php | 2 - .../V1/TransactionOptions_PartitionedDml.php | 16 - .../src/V1/TransactionOptions_ReadOnly.php | 16 - .../src/V1/TransactionOptions_ReadWrite.php | 16 - ...nsactionOptions_ReadWrite_ReadLockMode.php | 16 - Spanner/src/V1/Type.php | 10 +- Spanner/tests/OperationRefreshTrait.php | 41 - Spanner/tests/ResultGeneratorTrait.php | 141 +- Spanner/tests/Snippet/ArrayTypeTest.php | 68 +- Spanner/tests/Snippet/BackupTest.php | 169 +- .../tests/Snippet/Batch/BatchClientTest.php | 120 +- .../tests/Snippet/Batch/BatchSnapshotTest.php | 64 +- .../Snippet/Batch/QueryPartitionTest.php | 36 +- .../tests/Snippet/Batch/ReadPartitionTest.php | 38 +- Spanner/tests/Snippet/BatchDmlResultTest.php | 44 +- Spanner/tests/Snippet/CommitTimestampTest.php | 52 +- Spanner/tests/Snippet/DatabaseTest.php | 683 +++--- Spanner/tests/Snippet/DurationTest.php | 85 - .../Snippet/InstanceConfigurationTest.php | 98 +- Spanner/tests/Snippet/InstanceTest.php | 310 ++- Spanner/tests/Snippet/SnapshotTest.php | 9 +- Spanner/tests/Snippet/SpannerClientTest.php | 57 +- Spanner/tests/Snippet/StructTypeTest.php | 61 +- Spanner/tests/Snippet/StructValueTest.php | 78 +- Spanner/tests/Snippet/TransactionTest.php | 208 +- .../Snippet/TransactionalReadMethodsTest.php | 285 +-- Spanner/tests/StubCreationTrait.php | 38 - Spanner/tests/System/AdminTest.php | 11 +- Spanner/tests/System/BackupTest.php | 46 +- Spanner/tests/System/BatchTest.php | 55 - Spanner/tests/System/PgReadTest.php | 4 +- Spanner/tests/System/ReadTest.php | 4 +- Spanner/tests/System/SnapshotTest.php | 8 +- .../V1/Client/DatabaseAdminClientTest.php | 4 +- .../V1/Client/InstanceAdminClientTest.php | 4 +- Spanner/tests/Unit/BackupTest.php | 390 ++-- Spanner/tests/Unit/Batch/BatchClientTest.php | 153 +- .../tests/Unit/Batch/BatchSnapshotTest.php | 193 +- Spanner/tests/Unit/Connection/GrpcTest.php | 1690 -------------- .../tests/Unit/Connection/IamDatabaseTest.php | 69 - .../tests/Unit/Connection/IamInstanceTest.php | 69 - .../Connection/LongRunningConnectionTest.php | 70 - Spanner/tests/Unit/DatabaseTest.php | 2037 ++++++++++------- Spanner/tests/Unit/DurationTest.php | 66 - Spanner/tests/Unit/Fixtures.php | 5 - .../Unit/GapicBackoff/GapicBackoffTest.php | 114 - .../GapicBackoff/MockOperationResponse.php | 31 - .../tests/Unit/InstanceConfigurationTest.php | 289 ++- Spanner/tests/Unit/InstanceTest.php | 751 +++--- Spanner/tests/Unit/OperationTest.php | 486 ++-- Spanner/tests/Unit/ResultTest.php | 254 +- Spanner/tests/Unit/ResultTestTrait.php | 112 - .../Unit/Session/CacheSessionPoolTest.php | 25 +- Spanner/tests/Unit/SpannerClientTest.php | 365 +-- .../TransactionConfigurationTraitTest.php | 14 +- Spanner/tests/Unit/TransactionTest.php | 702 +++--- Spanner/tests/Unit/TransactionTypeTest.php | 866 ++++--- .../Unit/V1/Client/SpannerClientTest.php | 1 + .../V1/SpannerClientPartialVeneerTest.php | 45 - Spanner/tests/Unit/V1/SpannerClientTest.php | 1161 ---------- Spanner/tests/Unit/bootstrap.php | 12 + Spanner/tests/Unit/fixtures/instance.json | 7 - composer.json | 6 +- 267 files changed, 8122 insertions(+), 13038 deletions(-) create mode 100644 Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php create mode 100644 Spanner/MIGRATING.md delete mode 100644 Spanner/src/Admin/Database/V1/Backup_State.php delete mode 100644 Spanner/src/Admin/Database/V1/CopyBackupEncryptionConfig_EncryptionType.php delete mode 100644 Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig_EncryptionType.php delete mode 100644 Spanner/src/Admin/Database/V1/DatabaseAdminGrpcClient.php delete mode 100644 Spanner/src/Admin/Database/V1/Database_State.php delete mode 100644 Spanner/src/Admin/Database/V1/EncryptionInfo_Type.php delete mode 100644 Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig_EncryptionType.php delete mode 100644 Spanner/src/Admin/Instance/V1/InstanceAdminGrpcClient.php delete mode 100644 Spanner/src/Admin/Instance/V1/InstanceConfig_State.php delete mode 100644 Spanner/src/Admin/Instance/V1/InstanceConfig_Type.php delete mode 100644 Spanner/src/Admin/Instance/V1/Instance_State.php delete mode 100644 Spanner/src/Admin/Instance/V1/ReplicaInfo_ReplicaType.php delete mode 100644 Spanner/src/Connection/ConnectionInterface.php delete mode 100644 Spanner/src/Connection/Grpc.php delete mode 100644 Spanner/src/Connection/IamDatabase.php delete mode 100644 Spanner/src/Connection/IamInstance.php delete mode 100644 Spanner/src/Connection/LongRunningConnection.php delete mode 100644 Spanner/src/Duration.php rename Spanner/src/{RequestHeaderTrait.php => RequestTrait.php} (57%) delete mode 100644 Spanner/src/V1/CommitResponse_CommitStats.php delete mode 100644 Spanner/src/V1/ExecuteBatchDmlRequest_Statement.php delete mode 100644 Spanner/src/V1/ExecuteSqlRequest_QueryMode.php delete mode 100644 Spanner/src/V1/ExecuteSqlRequest_QueryOptions.php delete mode 100644 Spanner/src/V1/Mutation_Delete.php delete mode 100644 Spanner/src/V1/Mutation_Write.php delete mode 100644 Spanner/src/V1/PlanNode_ChildLink.php delete mode 100644 Spanner/src/V1/PlanNode_Kind.php delete mode 100644 Spanner/src/V1/PlanNode_ShortRepresentation.php delete mode 100644 Spanner/src/V1/RequestOptions_Priority.php delete mode 100644 Spanner/src/V1/SpannerGrpcClient.php delete mode 100644 Spanner/src/V1/StructType_Field.php delete mode 100644 Spanner/src/V1/TransactionOptions_PartitionedDml.php delete mode 100644 Spanner/src/V1/TransactionOptions_ReadOnly.php delete mode 100644 Spanner/src/V1/TransactionOptions_ReadWrite.php delete mode 100644 Spanner/src/V1/TransactionOptions_ReadWrite_ReadLockMode.php delete mode 100644 Spanner/tests/OperationRefreshTrait.php delete mode 100644 Spanner/tests/Snippet/DurationTest.php delete mode 100644 Spanner/tests/StubCreationTrait.php delete mode 100644 Spanner/tests/Unit/Connection/GrpcTest.php delete mode 100644 Spanner/tests/Unit/Connection/IamDatabaseTest.php delete mode 100644 Spanner/tests/Unit/Connection/IamInstanceTest.php delete mode 100644 Spanner/tests/Unit/Connection/LongRunningConnectionTest.php delete mode 100644 Spanner/tests/Unit/DurationTest.php delete mode 100644 Spanner/tests/Unit/GapicBackoff/GapicBackoffTest.php delete mode 100644 Spanner/tests/Unit/GapicBackoff/MockOperationResponse.php delete mode 100644 Spanner/tests/Unit/ResultTestTrait.php delete mode 100644 Spanner/tests/Unit/V1/SpannerClientPartialVeneerTest.php delete mode 100644 Spanner/tests/Unit/V1/SpannerClientTest.php create mode 100644 Spanner/tests/Unit/bootstrap.php delete mode 100644 Spanner/tests/Unit/fixtures/instance.json diff --git a/Core/src/ApiHelperTrait.php b/Core/src/ApiHelperTrait.php index d9900ea22944..8bc47049fef0 100644 --- a/Core/src/ApiHelperTrait.php +++ b/Core/src/ApiHelperTrait.php @@ -261,8 +261,8 @@ private function splitOptionalArgs(array $input, array $extraAllowedKeys = []) : $callOptionFields = array_keys((new CallOptions([]))->toArray()); $keys = array_merge($callOptionFields, $extraAllowedKeys); - $optionalArgs = $this->pluckArray($keys, $input); + $callOptions = $this->pluckArray($keys, $input); - return [$input, $optionalArgs]; + return [$input, $callOptions]; } } diff --git a/Core/src/Iam/Iam.php b/Core/src/Iam/Iam.php index 3d530d8a3fc2..cd351934da6d 100644 --- a/Core/src/Iam/Iam.php +++ b/Core/src/Iam/Iam.php @@ -34,7 +34,7 @@ * * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $instance = $spanner->instance('my-new-instance'); * * $iam = $instance->iam(); diff --git a/Core/src/RequestHandler.php b/Core/src/RequestHandler.php index 6e11b4d61e33..f66e026e672e 100644 --- a/Core/src/RequestHandler.php +++ b/Core/src/RequestHandler.php @@ -45,11 +45,11 @@ class RequestHandler */ private Serializer $serializer; - private array $clients; + private array $clients = []; /** * @param Serializer $serializer - * @param array $clientClasses + * @param array $clientClasses * @param array $clientConfig */ public function __construct( @@ -76,11 +76,14 @@ public function __construct( ); } //@codeCoverageIgnoreEnd - + // Initialize the client classes and store them in memory - $this->clients = []; - foreach ($clientClasses as $className) { - $this->clients[$className] = new $className($clientConfig); + foreach ($clientClasses as $client) { + if (is_object($client)) { + $this->clients[get_class($client)] = $client; + } else { + $this->clients[$client] = new $client($clientConfig); + } } } @@ -135,7 +138,7 @@ 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; } diff --git a/Core/src/RequestProcessorTrait.php b/Core/src/RequestProcessorTrait.php index 72440cd9194f..2f0dd1f41465 100644 --- a/Core/src/RequestProcessorTrait.php +++ b/Core/src/RequestProcessorTrait.php @@ -25,6 +25,7 @@ use \Google\Protobuf\Internal\Message; use Google\Rpc\RetryInfo; use Google\Rpc\BadRequest; +use GuzzleHttp\Promise\PromiseInterface; /** * @internal @@ -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) { @@ -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; } diff --git a/Core/src/ServiceBuilder.php b/Core/src/ServiceBuilder.php index 9a5ac3f4f822..786d74571163 100644 --- a/Core/src/ServiceBuilder.php +++ b/Core/src/ServiceBuilder.php @@ -249,7 +249,7 @@ public function pubsub(array $config = []) * * Example: * ``` - * $spanner = $cloud->spanner(); + * $spanner = $cloud->spanner(['projectId' => 'my-project']); * ``` * * @param array $config [optional] { diff --git a/Core/tests/Snippet/Iam/IamManagerTest.php b/Core/tests/Snippet/Iam/IamManagerTest.php index e41abaa14f2a..2731358b8495 100644 --- a/Core/tests/Snippet/Iam/IamManagerTest.php +++ b/Core/tests/Snippet/Iam/IamManagerTest.php @@ -36,7 +36,7 @@ class IamManagerTest extends SnippetTestCase private $policyData; private $resource; - private $requestHandler; + private $spannerClient; private $serializer; private $iam; diff --git a/Core/tests/Snippet/Iam/IamTest.php b/Core/tests/Snippet/Iam/IamTest.php index 4c9ae4988b61..24283448fe3a 100644 --- a/Core/tests/Snippet/Iam/IamTest.php +++ b/Core/tests/Snippet/Iam/IamTest.php @@ -19,6 +19,7 @@ use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Iam\Iam; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iam\IamConnectionInterface; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\SpannerClient; @@ -55,7 +56,7 @@ public function testClass() ]); $res = $snippet->invoke('iam'); - $this->assertInstanceOf(Iam::class, $res->returnVal()); + $this->assertInstanceOf(IamManager::class, $res->returnVal()); } public function testPolicy() diff --git a/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php b/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php new file mode 100644 index 000000000000..012ce33d9335 --- /dev/null +++ b/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php @@ -0,0 +1,420 @@ + self::METADATA_TYPE, + 'message' => RequestMetadata::class, + ], [ + 'typeUrl' => self::RESULT_TYPE, + 'message' => AuthorizationInfo::class, + ], + ]; + + public function setUp(): void + { + $this->requestHandler = $this->prophesize(RequestHandler::class); + $serializer = $this->prophesize(Serializer::class); + $serializer->encodeMessage(Argument::any())->will(function ($arg) { + if (is_bool($arg[0]) || is_array($arg[0])) { + return $arg[0]; + } + if (method_exists($arg[0], 'serializeToJsonString')) { + $json = $arg[0]->serializeToJsonString(); + return json_decode($json, true); + } + }); + $this->serializer = $serializer->reveal(); + $this->callables = [ + [ + 'typeUrl' => self::TYPE, + 'callable' => function ($res) { + return $res; + } + ] + ]; + $this->operation = TestHelpers::stub(LongRunningOperationManager::class, [ + $this->requestHandler->reveal(), + $this->serializer, + $this->callables, + $this->lroResponseMappers, + DatabaseAdminClient::class, + self::NAME, ); + } + + public function testName() + { + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'name'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('name'); + $this->assertEquals(self::NAME, $res->returnVal()); + } + + public function testDone() + { + $this->mockResumeOperation(); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'done'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke(); + $this->assertEquals('The operation is done!', $res->output()); + } + + public function testStateInProgress() + { + $this->mockResumeOperation(false); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); + $snippet->addLocal('operation', $this->operation); + $snippet->addUse(LongRunningOperationManager::class); + + $res = $snippet->invoke(); + $this->assertEquals('Operation is in progress', $res->output()); + } + + public function testStateDone() + { + $this->mockResumeOperation(); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); + $snippet->addUse(LongRunningOperationManager::class); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke(); + $this->assertEquals('Operation succeeded', $res->output()); + } + + public function testStateFailed() + { + $this->mockResumeOperation(true, false); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); + $snippet->addLocal('operation', $this->operation); + $snippet->addUse(LongRunningOperationManager::class); + + $res = $snippet->invoke(); + $this->assertEquals('Operation failed', $res->output()); + } + + public function testResult() + { + $result = [ + 'resource' => 'any', + 'permission' => 'all', + 'granted' => true, + ]; + $this->mockResumeOperation(); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'result'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('result'); + $this->assertEquals($result, $res->returnVal()); + } + + public function testError() + { + $result = [ + 'foo' => 'bar' + ]; + $this->mockResumeOperation(true, false, true); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'error'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('error'); + $this->assertEquals($result, $res->returnVal()); + } + + public function testInfo() + { + $result = [ + 'resource' => 'any', + 'permission' => 'all', + 'granted' => true, + ]; + $this->mockResumeOperation(); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'info'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('info'); + $this->assertEquals($result, $res->returnVal()['response']); + + $snippet->invoke(); + } + + public function testReload() + { + $result = [ + 'resource' => 'any', + 'permission' => 'all', + 'granted' => true, + ]; + $this->mockResumeOperation(true, true, false, 2); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'reload'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('result'); + $this->assertEquals($result, $res->returnVal()['response']); + + $snippet->invoke(); + } + + public function testPollUntilComplete() + { + $iteration = 0; + $result = [ + 'resource' => 'any', + 'permission' => 'all', + 'granted' => true, + ]; + $incompleteOp = $this->getOperationResponseMock(false); + $completeOp = $this->getOperationResponseMock(); + $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $this->requestHandler->getClientObject(Argument::any()) + ->shouldBeCalled() + ->willReturn($databaseAdminClient); + $databaseAdminClient->resumeOperation(Argument::that(function ($arg) use (&$iteration) { + $iteration++; + return $iteration == 1; + }), Argument::any())->willReturn($incompleteOp); + $databaseAdminClient->resumeOperation(Argument::that(function ($arg) use (&$iteration) { + return $iteration != 1; + }), Argument::any())->willReturn($completeOp); + + $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->operation->___setProperty('serializer', $this->serializer); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'pollUntilComplete'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke('result'); + $this->assertEquals($result, $res->returnVal()); + } + + public function testCancel() + { + $this->checkAndSkipTest([ + DatabaseAdminClient::class, + ]); + $operation = $this->prophesize(OperationResponse::class); + $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $operation->cancel(Argument::any())->shouldBeCalled(); + $this->requestHandler->getClientObject(Argument::any()) + ->shouldBeCalled() + ->willReturn($databaseAdminClient); + $databaseAdminClient->resumeOperation(Argument::cetera()) + ->willReturn($operation->reveal()); + + $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->operation->___setProperty('serializer', $this->serializer); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'cancel'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke(); + } + + public function testDelete() + { + $this->checkAndSkipTest([ + DatabaseAdminClient::class, + ]); + $operation = $this->prophesize(OperationResponse::class); + $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $operation->delete(Argument::any())->shouldBeCalled(); + $this->requestHandler->getClientObject(Argument::any()) + ->shouldBeCalled() + ->willReturn($databaseAdminClient); + $databaseAdminClient->resumeOperation(Argument::cetera()) + ->willReturn($operation->reveal()); + + $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->operation->___setProperty('serializer', $this->serializer); + + $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'delete'); + $snippet->addLocal('operation', $this->operation); + + $res = $snippet->invoke(); + } + + private function mockResumeOperation( + bool $done = true, + bool $result = true, + bool $error = false, + int $times = 1 + ) { + $operation = $this->getOperationResponseMock($done, $result, $error); + $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $this->requestHandler->getClientObject(Argument::any()) + ->shouldBeCalledTimes($times) + ->willReturn($databaseAdminClient); + $databaseAdminClient->resumeOperation(Argument::cetera())->willReturn($operation); + + $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->operation->___setProperty('serializer', $this->serializer); + } + + private function getOperationResponseMock( + bool $done = true, + bool $result = true, + bool $error = false, + int $times = 1 + ) { + $this->checkAndSkipTest([ + DatabaseAdminClient::class, + ]); + if ($result) { + $result = new AuthorizationInfo([ + 'resource' => 'any', + 'permission' => 'all', + 'granted' => true, + ]); + } + if ($error) { + $error = ['foo' => 'bar']; + } + $meta = new RequestMetadata([ + 'caller_ip' => '127.8.9.10', // Sic(!) + ]); + $response = new Response(self::METADATA_TYPE, $meta, $done, self::RESULT_TYPE, $result, $error); + $operation = new OperationResponse(self::OPERATION_NAME, null, ['lastProtoResponse' => $response]); + return $operation; + } +} + +//@codingStandardsIgnoreStart + +class Value +{ + public $value; + + public function __construct($value = null) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } +} + +class Response +{ + private $metadataType; + private $metadata; + private $responseType; + private $response = null; + private $error = null; + private $done = true; + + public function __construct( + $metaType, + $metadata, + $done = true, + $respType = null, + $response = null, + $error = null + ) { + $this->metadataType = $metaType; + $this->metadata = $metadata->serializeToString(); + $this->responseType = $respType; + $this->done = $done; + if ($response) { + $this->response = $response->serializeToString(); + } + $this->error = $error; + } + + public function getResponse() + { + return new Value($this->response); + } + + public function getMetadata() + { + return new Value($this->metadata); + } + + public function getDone() + { + return (isset($this->response) or isset($this->error)); + } + + public function getError() + { + return $this->error; + } + + public function serializeToJsonString() + { + $result = [ + 'done' => $this->done, + 'metadata' => [ + 'typeUrl' => $this->metadataType, + 'value' => $this->metadata, + ], + ]; + if (isset($this->response)) { + $result['response'] = [ + 'typeUrl' => $this->responseType, + 'value' => $this->response, + ]; + } + + return json_encode($result); + } +} diff --git a/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php b/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php index 89c35f11752f..21ee93ac7d5d 100644 --- a/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php +++ b/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php @@ -18,10 +18,14 @@ namespace Google\Cloud\Core\Tests\Unit\LongRunning; use Google\ApiCore\OperationResponse; +use Google\ApiCore\Serializer; use Google\Cloud\Core\LongRunning\OperationResponseTrait; use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\Cloud\Core\LongRunning\LongRunningOperationManager; use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\RequestHandler; +use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; +use Google\Cloud\Core\Testing\CheckForClassTrait; use Prophecy\Argument; use Google\Cloud\Audit\RequestMetadata; use Google\Cloud\Audit\AuthorizationInfo; @@ -34,6 +38,7 @@ */ class OperationResponseTraitTest extends TestCase { + use CheckForClassTrait; use ProphecyTrait; use OperationResponseTrait; diff --git a/Core/tests/Unit/ServiceBuilderTest.php b/Core/tests/Unit/ServiceBuilderTest.php index 3667628bb299..33ce724a6bd2 100644 --- a/Core/tests/Unit/ServiceBuilderTest.php +++ b/Core/tests/Unit/ServiceBuilderTest.php @@ -24,7 +24,6 @@ use Google\Cloud\Firestore\FirestoreClient; use Google\Cloud\Language\LanguageClient; use Google\Cloud\Logging\LoggingClient; -use Google\Cloud\Spanner\SpannerClient; use Google\Cloud\Speech\SpeechClient; use Google\Cloud\Storage\StorageClient; use Google\Cloud\Core\Tests\Unit\Fixtures; @@ -175,11 +174,6 @@ public function serviceProvider() ], [ 'language', LanguageClient::class - ], [ - 'spanner', - SpannerClient::class, - [], - [$this, 'checkAndSkipGrpcTests'] ], [ 'speech', SpeechClient::class, diff --git a/Spanner/MIGRATING.md b/Spanner/MIGRATING.md new file mode 100644 index 000000000000..ecc9dc7bfaf6 --- /dev/null +++ b/Spanner/MIGRATING.md @@ -0,0 +1,115 @@ +# Migrating Google Spanner from V1 to V2 + +## How to upgrade + +Update your `google/cloud-spanner` dependency to `^2.0`: + +``` +{ + "require": { + "google/cloud-spanner": "^2.0" + } +} +``` + +## Changes + +### Client Options changes + +The following client options are removed/replaced with other options present in +[`ClientOptions`][ClientOptions]. This was done to ensure client options are consistent across all +Google Cloud clients. + +- `authCache` -> Moved to `credentialsConfig.authCache` +- `authCacheOptions` -> Moved to `credentialsConfig.authCacheOptions` +- `FetchAuthTokenInterface` -> Moved to `credentials` +- `keyFile` -> Moved to `credentials` +- `keyFilePath` -> Moved to `credentials` +- `requestTimeout` -> Removed from client options and moved to a call option `timeoutMillis` +- `scopes` -> Moved to `credentialsConfig.scopes` +- `quotaProject` -> Moved to `credentialsConfig.quotaProject` +- `httpHandler` -> Moved to `transportConfig.rest.httpHandler` +- `authHttpHandler` -> Moved to `credentialsConfig.authHttpHandler` +- `retries` -> Removed from client options and moved to call options `retrySettings.maxRetries` + +### Retry Options changes + +The retry options have been moved to use [`RetrySettings`][RetrySettings] in call options +and function parameters. + +- `retries` -> Renamed to `retrySettings.maxRetries` +- `maxRetries` -> Renamed to `retrySettings.maxRetries` + +[RetrySettings]: https://googleapis.github.io/gax-php/v1.26.1/Google/ApiCore/RetrySettings.html + +[ClientOptions]: https://googleapis.github.io/gax-php/v1.26.1/Google/ApiCore/Options/ClientOptions.html + +### Connection classes are not used anymore. + +This is a major change with this major version but one that we hope won't break any users. When the +`SpannerClient` was created, behind the scenes a connection adapter was initialized. +This connection object was then forwarded to any resource classes internally, +like so: + +```php +// This initialized a connection object +$client = new SpannerClient(); +// This passed on the connection object to the Instance class +$instance = $spanner->instance('my-instance'); +``` + +As you can see the connection object was handled internally. If you used the library in this way, +you will not need to make any changes. However, if you created the connection classes directly +and passed it to the `Instance` class, this will break in Spanner `v2`: + +```php +// Not intended +$connObj = new Grpc([]); +$instance = new Instance( + $connObj, + // other constructor options +); +``` + +### `Google\Cloud\Spanner\Duration` class is not used anymore. +We have removed the `Google\Cloud\Spanner\Duration` class from the library. Instead we will be using the `Google\Protobuf\Duration` class. + +### IAM class changes + +We have kept the functionality of `IAM` the same, however the underlying `IAM` class has changed. +```php +// In V1, this used to return an instance of Google\Cloud\Core\Iam\Iam +$iam = $instance->iam(); + +// In V2, this will return an instance of Google\Cloud\Core\Iam\IamManager +$iam = $instance->iam(); + +// Both the classes share the same functionality, so the following methods will work for both versions. +$iam->policy(); +$iam->setPolicy(); +$iam->testIamPermissions(); +``` + +### LongRunningOperation class changes + +We have kept the functionality of `LongRunningOperation` the same, +however the underlying `LongRunningOperation` class has changed. +```php +// In V1, this used to return an instance of Google\Cloud\Core\LongRunning\LongRunningOperation. +$lro = $instance->create($configuration); + +// In V2, this will return an instance of Google\ApiCore\OperationResponse. +$lro = $instance->create($configuration); + +// Both the classes share the same functionality, so the following methods will work for both versions. +$lro->name(); +$lro->done(); +$lro->state(); +$lro->result(); +$lro->error(); +$lro->info(); +$lro->reload(); +$lro->pollUntilComplete(); +$lro->cancel(); +$lro->delete(); +``` diff --git a/Spanner/composer.json b/Spanner/composer.json index 6bd4c5577770..8e3b38a20de8 100644 --- a/Spanner/composer.json +++ b/Spanner/composer.json @@ -6,8 +6,8 @@ "require": { "php": "^8.0", "ext-grpc": "*", - "google/cloud-core": "^1.52.7", - "google/gax": "^1.34.0" + "google/cloud-core": "1.60", + "google/gax": "dev-result-function as 1.40.0" }, "require-dev": { "phpunit/phpunit": "^9.0", @@ -16,7 +16,9 @@ "phpdocumentor/reflection": "^5.3.3", "phpdocumentor/reflection-docblock": "^5.3", "erusev/parsedown": "^1.6", - "google/cloud-pubsub": "^2.0" + "google/cloud-pubsub": "^2.0", + "dg/bypass-finals": "^1.7", + "dms/phpunit-arraysubset-asserts": "^0.5.0" }, "suggest": { "ext-protobuf": "Provides a significant increase in throughput over the pure PHP protobuf implementation. See https://cloud.google.com/php/grpc for installation instructions.", @@ -40,5 +42,16 @@ "psr-4": { "Google\\Cloud\\Spanner\\Tests\\": "tests" } + }, + "repositories": { + "google-cloud": { + "type": "path", + "url": "../Core", + "options": { + "versions": { + "google/cloud-core": "1.60" + } + } + } } } diff --git a/Spanner/owlbot.py b/Spanner/owlbot.py index 84d9db0ddeb1..d1e761b3a56b 100644 --- a/Spanner/owlbot.py +++ b/Spanner/owlbot.py @@ -70,56 +70,15 @@ # copy GPBMetadata file to metadata s.move(admin_library / f'proto/src/GPBMetadata/Google/Spanner', f'metadata/', merge=php._merge) - -# Fix test namespaces -s.replace( - 'tests/Unit/Admin/Database/*/*.php', - r'namespace Google\\Cloud\\Spanner\\Admin\\Database\\Tests\\Unit', - r'namespace Google\\Cloud\\Spanner\\Tests\\Unit\\Admin\\Database') +# remove class_alias code s.replace( - 'tests/Unit/Admin/Instance/*/*.php', - r'namespace Google\\Cloud\\Spanner\\Admin\\Instance\\Tests\\Unit', - r'namespace Google\\Cloud\\Spanner\\Tests\\Unit\\Admin\\Instance') - -# fix test group -s.replace( - 'tests/**/Admin/Database/V1/*Test.php', - '@group database', - '@group spanner-admin-database') - -s.replace( - 'tests/**/Admin/Instance/V1/*Test.php', - '@group instance', - '@group spanner-admin-instance') - -# remove ReadOnly class_alias code -s.replace( - "src/V*/**/PBReadOnly.php", - r"^// Adding a class alias for backwards compatibility with the \"readonly\" keyword.$" + "src/V*/**/*.php", + r"^// Adding a class alias for backwards compatibility with the previous class name.$" + "\n" - + r"^class_alias\(PBReadOnly::class, __NAMESPACE__ . '\\ReadOnly'\);$" + + r"^class_alias\(.*\);$" + "\n", '') -### [START] protoc backwards compatibility fixes - -# roll back to private properties. -s.replace( - "src/**/V*/**/*.php", - r"Generated from protobuf field ([^\n]{0,})\n\s{5}\*/\n\s{4}protected \$", - r"""Generated from protobuf field \1 - */ - private $""") - -# Replace "Unwrapped" with "Value" for method names. -s.replace( - "src/**/V*/**/*.php", - r"public function ([s|g]\w{3,})Unwrapped", - r"public function \1Value" -) - -### [END] protoc backwards compatibility fixes - # fix relative cloud.google.com links s.replace( "src/**/V*/**/*.php", diff --git a/Spanner/phpunit.xml.dist b/Spanner/phpunit.xml.dist index 9923818f349f..33fc08b07b98 100644 --- a/Spanner/phpunit.xml.dist +++ b/Spanner/phpunit.xml.dist @@ -1,5 +1,5 @@ - + src diff --git a/Spanner/src/Admin/Database/V1/Backup.php b/Spanner/src/Admin/Database/V1/Backup.php index e91578bad1e6..fa62e2c3d8f9 100644 --- a/Spanner/src/Admin/Database/V1/Backup.php +++ b/Spanner/src/Admin/Database/V1/Backup.php @@ -24,7 +24,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 2 [(.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * The backup will contain an externally consistent copy of the database at * the timestamp specified by `version_time`. If `version_time` is not @@ -33,7 +33,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp version_time = 9; */ - private $version_time = null; + protected $version_time = null; /** * Required for the * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] @@ -45,7 +45,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp expire_time = 3; */ - private $expire_time = null; + protected $expire_time = null; /** * Output only for the * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] @@ -64,7 +64,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1; */ - private $name = ''; + protected $name = ''; /** * Output only. The time the * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] @@ -73,13 +73,13 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp create_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $create_time = null; + protected $create_time = null; /** * Output only. Size of the backup in bytes. * * Generated from protobuf field int64 size_bytes = 5 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $size_bytes = 0; + protected $size_bytes = 0; /** * Output only. The number of bytes that will be freed by deleting this * backup. This value will be zero if, for example, this backup is part of an @@ -108,7 +108,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.Backup.State state = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $state = 0; + protected $state = 0; /** * Output only. The names of the restored databases that reference the backup. * The database names are of @@ -126,7 +126,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.EncryptionInfo encryption_info = 8 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $encryption_info = null; + protected $encryption_info = null; /** * Output only. The encryption information for the backup, whether it is * protected by one or more KMS keys. The information includes all Cloud @@ -143,7 +143,7 @@ class Backup extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.DatabaseDialect database_dialect = 10 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $database_dialect = 0; + protected $database_dialect = 0; /** * Output only. The names of the destination backups being created by copying * this source backup. The backup names are of the form diff --git a/Spanner/src/Admin/Database/V1/BackupInfo.php b/Spanner/src/Admin/Database/V1/BackupInfo.php index daaf059905a8..301cdec6653a 100644 --- a/Spanner/src/Admin/Database/V1/BackupInfo.php +++ b/Spanner/src/Admin/Database/V1/BackupInfo.php @@ -20,7 +20,7 @@ class BackupInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string backup = 1 [(.google.api.resource_reference) = { */ - private $backup = ''; + protected $backup = ''; /** * The backup contains an externally consistent copy of `source_database` at * the timestamp specified by `version_time`. If the @@ -30,7 +30,7 @@ class BackupInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp version_time = 4; */ - private $version_time = null; + protected $version_time = null; /** * The time the * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] @@ -38,13 +38,13 @@ class BackupInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp create_time = 2; */ - private $create_time = null; + protected $create_time = null; /** * Name of the database the backup was created from. * * Generated from protobuf field string source_database = 3 [(.google.api.resource_reference) = { */ - private $source_database = ''; + protected $source_database = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/Backup_State.php b/Spanner/src/Admin/Database/V1/Backup_State.php deleted file mode 100644 index c284017a215d..000000000000 --- a/Spanner/src/Admin/Database/V1/Backup_State.php +++ /dev/null @@ -1,16 +0,0 @@ -.google.spanner.admin.database.v1.CopyBackupEncryptionConfig.EncryptionType encryption_type = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $encryption_type = 0; + protected $encryption_type = 0; /** * Optional. The Cloud KMS key that will be used to protect the backup. * This field should be set only when @@ -30,7 +30,7 @@ class CopyBackupEncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string kms_key_name = 2 [(.google.api.field_behavior) = OPTIONAL, (.google.api.resource_reference) = { */ - private $kms_key_name = ''; + protected $kms_key_name = ''; /** * Optional. Specifies the KMS configuration for the one or more keys used to * protect the backup. Values are of the form diff --git a/Spanner/src/Admin/Database/V1/CopyBackupEncryptionConfig_EncryptionType.php b/Spanner/src/Admin/Database/V1/CopyBackupEncryptionConfig_EncryptionType.php deleted file mode 100644 index 9bd0d193130f..000000000000 --- a/Spanner/src/Admin/Database/V1/CopyBackupEncryptionConfig_EncryptionType.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * The name of the source backup that is being copied. * Values are of the form @@ -31,7 +31,7 @@ class CopyBackupMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string source_backup = 2 [(.google.api.resource_reference) = { */ - private $source_backup = ''; + protected $source_backup = ''; /** * The progress of the * [CopyBackup][google.spanner.admin.database.v1.DatabaseAdmin.CopyBackup] @@ -39,7 +39,7 @@ class CopyBackupMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.OperationProgress progress = 3; */ - private $progress = null; + protected $progress = null; /** * The time at which cancellation of CopyBackup operation was received. * [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] @@ -56,7 +56,7 @@ class CopyBackupMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 4; */ - private $cancel_time = null; + protected $cancel_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/CopyBackupRequest.php b/Spanner/src/Admin/Database/V1/CopyBackupRequest.php index 5f378df44f18..8a80683e54fa 100644 --- a/Spanner/src/Admin/Database/V1/CopyBackupRequest.php +++ b/Spanner/src/Admin/Database/V1/CopyBackupRequest.php @@ -22,7 +22,7 @@ class CopyBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The id of the backup copy. * The `backup_id` appended to `parent` forms the full backup_uri of the form @@ -30,7 +30,7 @@ class CopyBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string backup_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $backup_id = ''; + protected $backup_id = ''; /** * Required. The source backup to be copied. * The source backup needs to be in READY state for it to be copied. @@ -41,7 +41,7 @@ class CopyBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string source_backup = 3 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $source_backup = ''; + protected $source_backup = ''; /** * Required. The expiration time of the backup in microsecond granularity. * The expiration time must be at least 6 hours and at most 366 days @@ -51,7 +51,7 @@ class CopyBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp expire_time = 4 [(.google.api.field_behavior) = REQUIRED]; */ - private $expire_time = null; + protected $expire_time = null; /** * Optional. The encryption configuration used to encrypt the backup. If this * field is not specified, the backup will use the same encryption @@ -61,7 +61,7 @@ class CopyBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.CopyBackupEncryptionConfig encryption_config = 5 [(.google.api.field_behavior) = OPTIONAL]; */ - private $encryption_config = null; + protected $encryption_config = null; /** * @param string $parent Required. The name of the destination instance that will contain the backup diff --git a/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig.php b/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig.php index 856296def13e..40ec928a4b04 100644 --- a/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig.php +++ b/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig.php @@ -20,7 +20,7 @@ class CreateBackupEncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.CreateBackupEncryptionConfig.EncryptionType encryption_type = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $encryption_type = 0; + protected $encryption_type = 0; /** * Optional. The Cloud KMS key that will be used to protect the backup. * This field should be set only when @@ -30,7 +30,7 @@ class CreateBackupEncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string kms_key_name = 2 [(.google.api.field_behavior) = OPTIONAL, (.google.api.resource_reference) = { */ - private $kms_key_name = ''; + protected $kms_key_name = ''; /** * Optional. Specifies the KMS configuration for the one or more keys used to * protect the backup. Values are of the form diff --git a/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig_EncryptionType.php b/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig_EncryptionType.php deleted file mode 100644 index 46d3b8504968..000000000000 --- a/Spanner/src/Admin/Database/V1/CreateBackupEncryptionConfig_EncryptionType.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * The name of the database the backup is created from. * * Generated from protobuf field string database = 2 [(.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * The progress of the * [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] @@ -35,7 +35,7 @@ class CreateBackupMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.OperationProgress progress = 3; */ - private $progress = null; + protected $progress = null; /** * The time at which cancellation of this operation was received. * [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] @@ -52,7 +52,7 @@ class CreateBackupMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 4; */ - private $cancel_time = null; + protected $cancel_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/CreateBackupRequest.php b/Spanner/src/Admin/Database/V1/CreateBackupRequest.php index 552eaff66df3..9c216c980f6f 100644 --- a/Spanner/src/Admin/Database/V1/CreateBackupRequest.php +++ b/Spanner/src/Admin/Database/V1/CreateBackupRequest.php @@ -26,7 +26,7 @@ class CreateBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The id of the backup to be created. The `backup_id` appended to * `parent` forms the full backup name of the form @@ -34,13 +34,13 @@ class CreateBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string backup_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $backup_id = ''; + protected $backup_id = ''; /** * Required. The backup to create. * * Generated from protobuf field .google.spanner.admin.database.v1.Backup backup = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $backup = null; + protected $backup = null; /** * Optional. The encryption configuration used to encrypt the backup. If this * field is not specified, the backup will use the same encryption @@ -50,7 +50,7 @@ class CreateBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.CreateBackupEncryptionConfig encryption_config = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $encryption_config = null; + protected $encryption_config = null; /** * @param string $parent Required. The name of the instance in which the backup will be diff --git a/Spanner/src/Admin/Database/V1/CreateDatabaseMetadata.php b/Spanner/src/Admin/Database/V1/CreateDatabaseMetadata.php index 1c30d36327dc..15e141e01574 100644 --- a/Spanner/src/Admin/Database/V1/CreateDatabaseMetadata.php +++ b/Spanner/src/Admin/Database/V1/CreateDatabaseMetadata.php @@ -21,7 +21,7 @@ class CreateDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/CreateDatabaseRequest.php b/Spanner/src/Admin/Database/V1/CreateDatabaseRequest.php index 3dbfcc5511cc..52e40ad3836f 100644 --- a/Spanner/src/Admin/Database/V1/CreateDatabaseRequest.php +++ b/Spanner/src/Admin/Database/V1/CreateDatabaseRequest.php @@ -22,7 +22,7 @@ class CreateDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. A `CREATE DATABASE` statement, which specifies the ID of the * new database. The database ID must conform to the regular expression @@ -32,7 +32,7 @@ class CreateDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string create_statement = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $create_statement = ''; + protected $create_statement = ''; /** * Optional. A list of DDL statements to run inside the newly created * database. Statements can create tables, indexes, etc. These @@ -49,13 +49,13 @@ class CreateDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.EncryptionConfig encryption_config = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $encryption_config = null; + protected $encryption_config = null; /** * Optional. The dialect of the Cloud Spanner Database. * * Generated from protobuf field .google.spanner.admin.database.v1.DatabaseDialect database_dialect = 5 [(.google.api.field_behavior) = OPTIONAL]; */ - private $database_dialect = 0; + protected $database_dialect = 0; /** * Optional. Proto descriptors used by CREATE/ALTER PROTO BUNDLE statements in * 'extra_statements' above. @@ -75,7 +75,7 @@ class CreateDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes proto_descriptors = 6 [(.google.api.field_behavior) = OPTIONAL]; */ - private $proto_descriptors = ''; + protected $proto_descriptors = ''; /** * @param string $parent Required. The name of the instance that will serve the new database. diff --git a/Spanner/src/Admin/Database/V1/Database.php b/Spanner/src/Admin/Database/V1/Database.php index a9b8a57a434e..7116c55f5d1a 100644 --- a/Spanner/src/Admin/Database/V1/Database.php +++ b/Spanner/src/Admin/Database/V1/Database.php @@ -24,26 +24,26 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $name = ''; + protected $name = ''; /** * Output only. The current database state. * * Generated from protobuf field .google.spanner.admin.database.v1.Database.State state = 2 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $state = 0; + protected $state = 0; /** * Output only. If exists, the time at which the database creation started. * * Generated from protobuf field .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $create_time = null; + protected $create_time = null; /** * Output only. Applicable only for restored databases. Contains information * about the restore source. * * Generated from protobuf field .google.spanner.admin.database.v1.RestoreInfo restore_info = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $restore_info = null; + protected $restore_info = null; /** * Output only. For databases that are using customer managed encryption, this * field contains the encryption configuration for the database. @@ -52,7 +52,7 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.EncryptionConfig encryption_config = 5 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $encryption_config = null; + protected $encryption_config = null; /** * Output only. For databases that are using customer managed encryption, this * field contains the encryption information for the database, such as @@ -75,7 +75,7 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string version_retention_period = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $version_retention_period = ''; + protected $version_retention_period = ''; /** * Output only. Earliest timestamp at which older versions of the data can be * read. This value is continuously updated by Cloud Spanner and becomes stale @@ -85,7 +85,7 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp earliest_version_time = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $earliest_version_time = null; + protected $earliest_version_time = null; /** * Output only. The read-write region which contains the database's leader * replicas. @@ -95,13 +95,13 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string default_leader = 9 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $default_leader = ''; + protected $default_leader = ''; /** * Output only. The dialect of the Cloud Spanner Database. * * Generated from protobuf field .google.spanner.admin.database.v1.DatabaseDialect database_dialect = 10 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $database_dialect = 0; + protected $database_dialect = 0; /** * Whether drop protection is enabled for this database. Defaults to false, * if not set. For more details, please see how to [prevent accidental @@ -110,14 +110,14 @@ class Database extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool enable_drop_protection = 11; */ - private $enable_drop_protection = false; + protected $enable_drop_protection = false; /** * Output only. If true, the database is being updated. If false, there are no * ongoing update operations for the database. * * Generated from protobuf field bool reconciling = 12 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $reconciling = false; + protected $reconciling = false; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/DatabaseAdminGrpcClient.php b/Spanner/src/Admin/Database/V1/DatabaseAdminGrpcClient.php deleted file mode 100644 index c62e9062faa4..000000000000 --- a/Spanner/src/Admin/Database/V1/DatabaseAdminGrpcClient.php +++ /dev/null @@ -1,473 +0,0 @@ -_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/ListDatabases', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesResponse', 'decode'], - $metadata, $options); - } - - /** - * Creates a new Cloud Spanner database and starts to prepare it for serving. - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format `/operations/` and - * can be used to track preparation of the database. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [CreateDatabaseMetadata][google.spanner.admin.database.v1.CreateDatabaseMetadata]. The - * [response][google.longrunning.Operation.response] field type is - * [Database][google.spanner.admin.database.v1.Database], if successful. - * @param \Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function CreateDatabase(\Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/CreateDatabase', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Gets the state of a Cloud Spanner database. - * @param \Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetDatabase(\Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/GetDatabase', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\Database', 'decode'], - $metadata, $options); - } - - /** - * Updates a Cloud Spanner database. The returned - * [long-running operation][google.longrunning.Operation] can be used to track - * the progress of updating the database. If the named database does not - * exist, returns `NOT_FOUND`. - * - * While the operation is pending: - * - * * The database's - * [reconciling][google.spanner.admin.database.v1.Database.reconciling] - * field is set to true. - * * Cancelling the operation is best-effort. If the cancellation succeeds, - * the operation metadata's - * [cancel_time][google.spanner.admin.database.v1.UpdateDatabaseMetadata.cancel_time] - * is set, the updates are reverted, and the operation terminates with a - * `CANCELLED` status. - * * New UpdateDatabase requests will return a `FAILED_PRECONDITION` error - * until the pending operation is done (returns successfully or with - * error). - * * Reading the database via the API continues to give the pre-request - * values. - * - * Upon completion of the returned operation: - * - * * The new values are in effect and readable via the API. - * * The database's - * [reconciling][google.spanner.admin.database.v1.Database.reconciling] - * field becomes false. - * - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format - * `projects//instances//databases//operations/` - * and can be used to track the database modification. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [UpdateDatabaseMetadata][google.spanner.admin.database.v1.UpdateDatabaseMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [Database][google.spanner.admin.database.v1.Database], if successful. - * @param \Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function UpdateDatabase(\Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/UpdateDatabase', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Updates the schema of a Cloud Spanner database by - * creating/altering/dropping tables, columns, indexes, etc. The returned - * [long-running operation][google.longrunning.Operation] will have a name of - * the format `/operations/` and can be used to - * track execution of the schema change(s). The - * [metadata][google.longrunning.Operation.metadata] field type is - * [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. The operation has no response. - * @param \Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function UpdateDatabaseDdl(\Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/UpdateDatabaseDdl', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Drops (aka deletes) a Cloud Spanner database. - * Completed backups for the database will be retained according to their - * `expire_time`. - * Note: Cloud Spanner might continue to accept requests for a few seconds - * after the database has been deleted. - * @param \Google\Cloud\Spanner\Admin\Database\V1\DropDatabaseRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function DropDatabase(\Google\Cloud\Spanner\Admin\Database\V1\DropDatabaseRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/DropDatabase', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Returns the schema of a Cloud Spanner database as a list of formatted - * DDL statements. This method does not show pending schema updates, those may - * be queried using the [Operations][google.longrunning.Operations] API. - * @param \Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetDatabaseDdl(\Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/GetDatabaseDdl', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlResponse', 'decode'], - $metadata, $options); - } - - /** - * Sets the access control policy on a database or backup resource. - * Replaces any existing policy. - * - * Authorization requires `spanner.databases.setIamPolicy` - * permission on [resource][google.iam.v1.SetIamPolicyRequest.resource]. - * For backups, authorization requires `spanner.backups.setIamPolicy` - * permission on [resource][google.iam.v1.SetIamPolicyRequest.resource]. - * @param \Google\Cloud\Iam\V1\SetIamPolicyRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function SetIamPolicy(\Google\Cloud\Iam\V1\SetIamPolicyRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/SetIamPolicy', - $argument, - ['\Google\Cloud\Iam\V1\Policy', 'decode'], - $metadata, $options); - } - - /** - * Gets the access control policy for a database or backup resource. - * Returns an empty policy if a database or backup exists but does not have a - * policy set. - * - * Authorization requires `spanner.databases.getIamPolicy` permission on - * [resource][google.iam.v1.GetIamPolicyRequest.resource]. - * For backups, authorization requires `spanner.backups.getIamPolicy` - * permission on [resource][google.iam.v1.GetIamPolicyRequest.resource]. - * @param \Google\Cloud\Iam\V1\GetIamPolicyRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetIamPolicy(\Google\Cloud\Iam\V1\GetIamPolicyRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/GetIamPolicy', - $argument, - ['\Google\Cloud\Iam\V1\Policy', 'decode'], - $metadata, $options); - } - - /** - * Returns permissions that the caller has on the specified database or backup - * resource. - * - * Attempting this RPC on a non-existent Cloud Spanner database will - * result in a NOT_FOUND error if the user has - * `spanner.databases.list` permission on the containing Cloud - * Spanner instance. Otherwise returns an empty set of permissions. - * Calling this method on a backup that does not exist will - * result in a NOT_FOUND error if the user has - * `spanner.backups.list` permission on the containing instance. - * @param \Google\Cloud\Iam\V1\TestIamPermissionsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function TestIamPermissions(\Google\Cloud\Iam\V1\TestIamPermissionsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/TestIamPermissions', - $argument, - ['\Google\Cloud\Iam\V1\TestIamPermissionsResponse', 'decode'], - $metadata, $options); - } - - /** - * Starts creating a new Cloud Spanner Backup. - * The returned backup [long-running operation][google.longrunning.Operation] - * will have a name of the format - * `projects//instances//backups//operations/` - * and can be used to track creation of the backup. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. The - * [response][google.longrunning.Operation.response] field type is - * [Backup][google.spanner.admin.database.v1.Backup], if successful. Cancelling the returned operation will stop the - * creation and delete the backup. - * There can be only one pending backup creation per database. Backup creation - * of different databases can run concurrently. - * @param \Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function CreateBackup(\Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/CreateBackup', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Starts copying a Cloud Spanner Backup. - * The returned backup [long-running operation][google.longrunning.Operation] - * will have a name of the format - * `projects//instances//backups//operations/` - * and can be used to track copying of the backup. The operation is associated - * with the destination backup. - * The [metadata][google.longrunning.Operation.metadata] field type is - * [CopyBackupMetadata][google.spanner.admin.database.v1.CopyBackupMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [Backup][google.spanner.admin.database.v1.Backup], if successful. Cancelling the returned operation will stop the - * copying and delete the backup. - * Concurrent CopyBackup requests can run on the same source backup. - * @param \Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function CopyBackup(\Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/CopyBackup', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Gets metadata on a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. - * @param \Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetBackup(\Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/GetBackup', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\Backup', 'decode'], - $metadata, $options); - } - - /** - * Updates a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. - * @param \Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function UpdateBackup(\Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/UpdateBackup', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\Backup', 'decode'], - $metadata, $options); - } - - /** - * Deletes a pending or completed [Backup][google.spanner.admin.database.v1.Backup]. - * @param \Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function DeleteBackup(\Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/DeleteBackup', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Lists completed and pending backups. - * Backups returned are ordered by `create_time` in descending order, - * starting from the most recent `create_time`. - * @param \Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListBackups(\Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/ListBackups', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse', 'decode'], - $metadata, $options); - } - - /** - * Create a new database by restoring from a completed backup. The new - * database must be in the same project and in an instance with the same - * instance configuration as the instance containing - * the backup. The returned database [long-running - * operation][google.longrunning.Operation] has a name of the format - * `projects//instances//databases//operations/`, - * and can be used to track the progress of the operation, and to cancel it. - * The [metadata][google.longrunning.Operation.metadata] field type is - * [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. - * The [response][google.longrunning.Operation.response] type - * is [Database][google.spanner.admin.database.v1.Database], if - * successful. Cancelling the returned operation will stop the restore and - * delete the database. - * There can be only one database being restored into an instance at a time. - * Once the restore operation completes, a new restore operation can be - * initiated, without waiting for the optimize operation associated with the - * first restore to complete. - * @param \Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function RestoreDatabase(\Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/RestoreDatabase', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Lists database [longrunning-operations][google.longrunning.Operation]. - * A database operation has a name of the form - * `projects//instances//databases//operations/`. - * The long-running operation - * [metadata][google.longrunning.Operation.metadata] field type - * `metadata.type_url` describes the type of the metadata. Operations returned - * include those that have completed/failed/canceled within the last 7 days, - * and pending operations. - * @param \Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListDatabaseOperations(\Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/ListDatabaseOperations', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsResponse', 'decode'], - $metadata, $options); - } - - /** - * Lists the backup [long-running operations][google.longrunning.Operation] in - * the given instance. A backup operation has a name of the form - * `projects//instances//backups//operations/`. - * The long-running operation - * [metadata][google.longrunning.Operation.metadata] field type - * `metadata.type_url` describes the type of the metadata. Operations returned - * include those that have completed/failed/canceled within the last 7 days, - * and pending operations. Operations returned are ordered by - * `operation.metadata.value.progress.start_time` in descending order starting - * from the most recently started operation. - * @param \Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListBackupOperations(\Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/ListBackupOperations', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsResponse', 'decode'], - $metadata, $options); - } - - /** - * Lists Cloud Spanner database roles. - * @param \Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseRolesRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListDatabaseRoles(\Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseRolesRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.database.v1.DatabaseAdmin/ListDatabaseRoles', - $argument, - ['\Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseRolesResponse', 'decode'], - $metadata, $options); - } - -} diff --git a/Spanner/src/Admin/Database/V1/DatabaseRole.php b/Spanner/src/Admin/Database/V1/DatabaseRole.php index f7e32c23f83a..f34003461bac 100644 --- a/Spanner/src/Admin/Database/V1/DatabaseRole.php +++ b/Spanner/src/Admin/Database/V1/DatabaseRole.php @@ -22,7 +22,7 @@ class DatabaseRole extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $name = ''; + protected $name = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/Database_State.php b/Spanner/src/Admin/Database/V1/Database_State.php deleted file mode 100644 index 7aa7a007185b..000000000000 --- a/Spanner/src/Admin/Database/V1/Database_State.php +++ /dev/null @@ -1,16 +0,0 @@ -string action = 1; */ - private $action = ''; + protected $action = ''; /** * The entity type for the DDL statement, e.g. TABLE, INDEX, VIEW, etc. * This field can be empty string for some DDL statement, @@ -31,7 +31,7 @@ class DdlStatementActionInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string entity_type = 2; */ - private $entity_type = ''; + protected $entity_type = ''; /** * The entity name(s) being operated on the DDL statement. * E.g. diff --git a/Spanner/src/Admin/Database/V1/DeleteBackupRequest.php b/Spanner/src/Admin/Database/V1/DeleteBackupRequest.php index f8ae101ba9d9..a2418220b700 100644 --- a/Spanner/src/Admin/Database/V1/DeleteBackupRequest.php +++ b/Spanner/src/Admin/Database/V1/DeleteBackupRequest.php @@ -23,7 +23,7 @@ class DeleteBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. Name of the backup to delete. diff --git a/Spanner/src/Admin/Database/V1/DropDatabaseRequest.php b/Spanner/src/Admin/Database/V1/DropDatabaseRequest.php index 993cbe1e73ee..29d92fc6d153 100644 --- a/Spanner/src/Admin/Database/V1/DropDatabaseRequest.php +++ b/Spanner/src/Admin/Database/V1/DropDatabaseRequest.php @@ -21,7 +21,7 @@ class DropDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * @param string $database Required. The database to be dropped. Please see diff --git a/Spanner/src/Admin/Database/V1/EncryptionConfig.php b/Spanner/src/Admin/Database/V1/EncryptionConfig.php index 62529b83264e..c0bc45190b62 100644 --- a/Spanner/src/Admin/Database/V1/EncryptionConfig.php +++ b/Spanner/src/Admin/Database/V1/EncryptionConfig.php @@ -22,7 +22,7 @@ class EncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string kms_key_name = 2 [(.google.api.resource_reference) = { */ - private $kms_key_name = ''; + protected $kms_key_name = ''; /** * Specifies the KMS configuration for the one or more keys used to encrypt * the database. Values are of the form diff --git a/Spanner/src/Admin/Database/V1/EncryptionInfo.php b/Spanner/src/Admin/Database/V1/EncryptionInfo.php index 38ae91890389..960ada451ae8 100644 --- a/Spanner/src/Admin/Database/V1/EncryptionInfo.php +++ b/Spanner/src/Admin/Database/V1/EncryptionInfo.php @@ -20,7 +20,7 @@ class EncryptionInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.EncryptionInfo.Type encryption_type = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $encryption_type = 0; + protected $encryption_type = 0; /** * Output only. If present, the status of a recent encrypt/decrypt call on * underlying data for this database or backup. Regardless of status, data is @@ -28,14 +28,14 @@ class EncryptionInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.rpc.Status encryption_status = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $encryption_status = null; + protected $encryption_status = null; /** * Output only. A Cloud KMS key version that is being used to protect the * database or backup. * * Generated from protobuf field string kms_key_version = 2 [(.google.api.field_behavior) = OUTPUT_ONLY, (.google.api.resource_reference) = { */ - private $kms_key_version = ''; + protected $kms_key_version = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/EncryptionInfo_Type.php b/Spanner/src/Admin/Database/V1/EncryptionInfo_Type.php deleted file mode 100644 index 069d2c13182c..000000000000 --- a/Spanner/src/Admin/Database/V1/EncryptionInfo_Type.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. Name of the backup. diff --git a/Spanner/src/Admin/Database/V1/GetDatabaseDdlRequest.php b/Spanner/src/Admin/Database/V1/GetDatabaseDdlRequest.php index 746e1e6d300f..6e955af72d1a 100644 --- a/Spanner/src/Admin/Database/V1/GetDatabaseDdlRequest.php +++ b/Spanner/src/Admin/Database/V1/GetDatabaseDdlRequest.php @@ -23,7 +23,7 @@ class GetDatabaseDdlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * @param string $database Required. The database whose schema we wish to get. diff --git a/Spanner/src/Admin/Database/V1/GetDatabaseDdlResponse.php b/Spanner/src/Admin/Database/V1/GetDatabaseDdlResponse.php index fc346e69229d..a190d7f20b13 100644 --- a/Spanner/src/Admin/Database/V1/GetDatabaseDdlResponse.php +++ b/Spanner/src/Admin/Database/V1/GetDatabaseDdlResponse.php @@ -32,7 +32,7 @@ class GetDatabaseDdlResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes proto_descriptors = 2; */ - private $proto_descriptors = ''; + protected $proto_descriptors = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/GetDatabaseRequest.php b/Spanner/src/Admin/Database/V1/GetDatabaseRequest.php index dd3f68a8fa17..bacc542101f3 100644 --- a/Spanner/src/Admin/Database/V1/GetDatabaseRequest.php +++ b/Spanner/src/Admin/Database/V1/GetDatabaseRequest.php @@ -22,7 +22,7 @@ class GetDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the requested database. Values are of the form diff --git a/Spanner/src/Admin/Database/V1/ListBackupOperationsRequest.php b/Spanner/src/Admin/Database/V1/ListBackupOperationsRequest.php index 5c50db837408..c12ea71b9f7e 100644 --- a/Spanner/src/Admin/Database/V1/ListBackupOperationsRequest.php +++ b/Spanner/src/Admin/Database/V1/ListBackupOperationsRequest.php @@ -22,7 +22,7 @@ class ListBackupOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * An expression that filters the list of returned backup operations. * A filter expression consists of a field name, a @@ -91,14 +91,14 @@ class ListBackupOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string filter = 2; */ - private $filter = ''; + protected $filter = ''; /** * Number of operations to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.database.v1.ListBackupOperationsResponse.next_page_token] @@ -108,7 +108,7 @@ class ListBackupOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 4; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The instance of the backup operations. Values are of diff --git a/Spanner/src/Admin/Database/V1/ListBackupOperationsResponse.php b/Spanner/src/Admin/Database/V1/ListBackupOperationsResponse.php index 5e7b2c82b7d6..145888a176cd 100644 --- a/Spanner/src/Admin/Database/V1/ListBackupOperationsResponse.php +++ b/Spanner/src/Admin/Database/V1/ListBackupOperationsResponse.php @@ -37,7 +37,7 @@ class ListBackupOperationsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/ListBackupsRequest.php b/Spanner/src/Admin/Database/V1/ListBackupsRequest.php index d7e40757cb6f..cdcfabb13d44 100644 --- a/Spanner/src/Admin/Database/V1/ListBackupsRequest.php +++ b/Spanner/src/Admin/Database/V1/ListBackupsRequest.php @@ -22,7 +22,7 @@ class ListBackupsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * An expression that filters the list of returned backups. * A filter expression consists of a field name, a comparison operator, and a @@ -61,14 +61,14 @@ class ListBackupsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string filter = 2; */ - private $filter = ''; + protected $filter = ''; /** * Number of backups to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.database.v1.ListBackupsResponse.next_page_token] @@ -78,7 +78,7 @@ class ListBackupsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 4; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The instance to list backups from. Values are of the diff --git a/Spanner/src/Admin/Database/V1/ListBackupsResponse.php b/Spanner/src/Admin/Database/V1/ListBackupsResponse.php index 2e99679b56a5..97493b3614f1 100644 --- a/Spanner/src/Admin/Database/V1/ListBackupsResponse.php +++ b/Spanner/src/Admin/Database/V1/ListBackupsResponse.php @@ -30,7 +30,7 @@ class ListBackupsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/ListDatabaseOperationsRequest.php b/Spanner/src/Admin/Database/V1/ListDatabaseOperationsRequest.php index 5061df0e46b2..b080b3ec6f4c 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabaseOperationsRequest.php +++ b/Spanner/src/Admin/Database/V1/ListDatabaseOperationsRequest.php @@ -22,7 +22,7 @@ class ListDatabaseOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * An expression that filters the list of returned operations. * A filter expression consists of a field name, a @@ -66,14 +66,14 @@ class ListDatabaseOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string filter = 2; */ - private $filter = ''; + protected $filter = ''; /** * Number of operations to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.database.v1.ListDatabaseOperationsResponse.next_page_token] @@ -83,7 +83,7 @@ class ListDatabaseOperationsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 4; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The instance of the database operations. diff --git a/Spanner/src/Admin/Database/V1/ListDatabaseOperationsResponse.php b/Spanner/src/Admin/Database/V1/ListDatabaseOperationsResponse.php index bc32ed6930c5..5e6f45741b25 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabaseOperationsResponse.php +++ b/Spanner/src/Admin/Database/V1/ListDatabaseOperationsResponse.php @@ -33,7 +33,7 @@ class ListDatabaseOperationsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/ListDatabaseRolesRequest.php b/Spanner/src/Admin/Database/V1/ListDatabaseRolesRequest.php index d83ec6bf40c0..789226a8d1cd 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabaseRolesRequest.php +++ b/Spanner/src/Admin/Database/V1/ListDatabaseRolesRequest.php @@ -23,14 +23,14 @@ class ListDatabaseRolesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Number of database roles to be returned in the response. If 0 or less, * defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 2; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.database.v1.ListDatabaseRolesResponse.next_page_token] @@ -39,7 +39,7 @@ class ListDatabaseRolesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 3; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The database whose roles should be listed. diff --git a/Spanner/src/Admin/Database/V1/ListDatabaseRolesResponse.php b/Spanner/src/Admin/Database/V1/ListDatabaseRolesResponse.php index fc36896517be..e2f2c68f908e 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabaseRolesResponse.php +++ b/Spanner/src/Admin/Database/V1/ListDatabaseRolesResponse.php @@ -29,7 +29,7 @@ class ListDatabaseRolesResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/ListDatabasesRequest.php b/Spanner/src/Admin/Database/V1/ListDatabasesRequest.php index 919d73db0cbd..647992bff82d 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabasesRequest.php +++ b/Spanner/src/Admin/Database/V1/ListDatabasesRequest.php @@ -22,14 +22,14 @@ class ListDatabasesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Number of databases to be returned in the response. If 0 or less, * defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.database.v1.ListDatabasesResponse.next_page_token] @@ -38,7 +38,7 @@ class ListDatabasesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 4; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The instance whose databases should be listed. diff --git a/Spanner/src/Admin/Database/V1/ListDatabasesResponse.php b/Spanner/src/Admin/Database/V1/ListDatabasesResponse.php index c167bf56afd0..91e10c3ba675 100644 --- a/Spanner/src/Admin/Database/V1/ListDatabasesResponse.php +++ b/Spanner/src/Admin/Database/V1/ListDatabasesResponse.php @@ -29,7 +29,7 @@ class ListDatabasesResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/OperationProgress.php b/Spanner/src/Admin/Database/V1/OperationProgress.php index e5ea82b400d5..457e0f5b87d2 100644 --- a/Spanner/src/Admin/Database/V1/OperationProgress.php +++ b/Spanner/src/Admin/Database/V1/OperationProgress.php @@ -22,20 +22,20 @@ class OperationProgress extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 progress_percent = 1; */ - private $progress_percent = 0; + protected $progress_percent = 0; /** * Time the request was received. * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * If set, the time at which this operation failed or was completed * successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 3; */ - private $end_time = null; + protected $end_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/OptimizeRestoredDatabaseMetadata.php b/Spanner/src/Admin/Database/V1/OptimizeRestoredDatabaseMetadata.php index 99c53601ba38..6030a18943a5 100644 --- a/Spanner/src/Admin/Database/V1/OptimizeRestoredDatabaseMetadata.php +++ b/Spanner/src/Admin/Database/V1/OptimizeRestoredDatabaseMetadata.php @@ -23,13 +23,13 @@ class OptimizeRestoredDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * The progress of the post-restore optimizations. * * Generated from protobuf field .google.spanner.admin.database.v1.OperationProgress progress = 2; */ - private $progress = null; + protected $progress = null; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig.php b/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig.php index 54f3e913a322..7826049bac09 100644 --- a/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig.php +++ b/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig.php @@ -20,7 +20,7 @@ class RestoreDatabaseEncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType encryption_type = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $encryption_type = 0; + protected $encryption_type = 0; /** * Optional. The Cloud KMS key that will be used to encrypt/decrypt the * restored database. This field should be set only when @@ -30,7 +30,7 @@ class RestoreDatabaseEncryptionConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string kms_key_name = 2 [(.google.api.field_behavior) = OPTIONAL, (.google.api.resource_reference) = { */ - private $kms_key_name = ''; + protected $kms_key_name = ''; /** * Optional. Specifies the KMS configuration for the one or more keys used to * encrypt the database. Values are of the form diff --git a/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig_EncryptionType.php b/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig_EncryptionType.php deleted file mode 100644 index 6ddb496117ef..000000000000 --- a/Spanner/src/Admin/Database/V1/RestoreDatabaseEncryptionConfig_EncryptionType.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * The type of the restore source. * * Generated from protobuf field .google.spanner.admin.database.v1.RestoreSourceType source_type = 2; */ - private $source_type = 0; + protected $source_type = 0; /** * The progress of the * [RestoreDatabase][google.spanner.admin.database.v1.DatabaseAdmin.RestoreDatabase] @@ -35,7 +35,7 @@ class RestoreDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.OperationProgress progress = 4; */ - private $progress = null; + protected $progress = null; /** * The time at which cancellation of this operation was received. * [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] @@ -52,7 +52,7 @@ class RestoreDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 5; */ - private $cancel_time = null; + protected $cancel_time = null; /** * If exists, the name of the long-running operation that will be used to * track the post-restore optimization process to optimize the performance of @@ -68,7 +68,7 @@ class RestoreDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string optimize_database_operation_name = 6; */ - private $optimize_database_operation_name = ''; + protected $optimize_database_operation_name = ''; protected $source_info; /** diff --git a/Spanner/src/Admin/Database/V1/RestoreDatabaseRequest.php b/Spanner/src/Admin/Database/V1/RestoreDatabaseRequest.php index e9f2ef8b7d8c..7e9f39516096 100644 --- a/Spanner/src/Admin/Database/V1/RestoreDatabaseRequest.php +++ b/Spanner/src/Admin/Database/V1/RestoreDatabaseRequest.php @@ -25,7 +25,7 @@ class RestoreDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The id of the database to create and restore to. This * database must not already exist. The `database_id` appended to @@ -34,7 +34,7 @@ class RestoreDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $database_id = ''; + protected $database_id = ''; /** * Optional. An encryption configuration describing the encryption type and * key resources in Cloud KMS used to encrypt/decrypt the database to restore @@ -45,7 +45,7 @@ class RestoreDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig encryption_config = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $encryption_config = null; + protected $encryption_config = null; protected $source; /** diff --git a/Spanner/src/Admin/Database/V1/RestoreInfo.php b/Spanner/src/Admin/Database/V1/RestoreInfo.php index 97e9c5168127..2bcfe27d1868 100644 --- a/Spanner/src/Admin/Database/V1/RestoreInfo.php +++ b/Spanner/src/Admin/Database/V1/RestoreInfo.php @@ -20,7 +20,7 @@ class RestoreInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.RestoreSourceType source_type = 1; */ - private $source_type = 0; + protected $source_type = 0; protected $source_info; /** diff --git a/Spanner/src/Admin/Database/V1/UpdateBackupRequest.php b/Spanner/src/Admin/Database/V1/UpdateBackupRequest.php index 94db96b17083..0c76d8690c85 100644 --- a/Spanner/src/Admin/Database/V1/UpdateBackupRequest.php +++ b/Spanner/src/Admin/Database/V1/UpdateBackupRequest.php @@ -24,7 +24,7 @@ class UpdateBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.Backup backup = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $backup = null; + protected $backup = null; /** * Required. A mask specifying which fields (e.g. `expire_time`) in the * Backup resource should be updated. This mask is relative to the Backup @@ -34,7 +34,7 @@ class UpdateBackupRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.FieldMask update_mask = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $update_mask = null; + protected $update_mask = null; /** * @param \Google\Cloud\Spanner\Admin\Database\V1\Backup $backup Required. The backup to update. `backup.name`, and the fields to be updated diff --git a/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlMetadata.php b/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlMetadata.php index 07d475e07c0d..767766fad333 100644 --- a/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlMetadata.php +++ b/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlMetadata.php @@ -21,7 +21,7 @@ class UpdateDatabaseDdlMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * For an update this list contains all the statements. For an * individual statement, this list contains only that statement. @@ -44,7 +44,7 @@ class UpdateDatabaseDdlMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool throttled = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $throttled = false; + protected $throttled = false; /** * The progress of the * [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl] diff --git a/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlRequest.php b/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlRequest.php index 9e9e7ff08124..d520012a1ad1 100644 --- a/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlRequest.php +++ b/Spanner/src/Admin/Database/V1/UpdateDatabaseDdlRequest.php @@ -34,7 +34,7 @@ class UpdateDatabaseDdlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * Required. DDL statements to be applied to the database. * @@ -64,7 +64,7 @@ class UpdateDatabaseDdlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string operation_id = 3; */ - private $operation_id = ''; + protected $operation_id = ''; /** * Optional. Proto descriptors used by CREATE/ALTER PROTO BUNDLE statements. * Contains a protobuf-serialized @@ -83,7 +83,7 @@ class UpdateDatabaseDdlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes proto_descriptors = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $proto_descriptors = ''; + protected $proto_descriptors = ''; /** * @param string $database Required. The database to update. Please see diff --git a/Spanner/src/Admin/Database/V1/UpdateDatabaseMetadata.php b/Spanner/src/Admin/Database/V1/UpdateDatabaseMetadata.php index 52d8a69cb546..19c926a4aa5e 100644 --- a/Spanner/src/Admin/Database/V1/UpdateDatabaseMetadata.php +++ b/Spanner/src/Admin/Database/V1/UpdateDatabaseMetadata.php @@ -22,7 +22,7 @@ class UpdateDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.UpdateDatabaseRequest request = 1; */ - private $request = null; + protected $request = null; /** * The progress of the * [UpdateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase] @@ -30,14 +30,14 @@ class UpdateDatabaseMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.OperationProgress progress = 2; */ - private $progress = null; + protected $progress = null; /** * The time at which this operation was cancelled. If set, this operation is * in the process of undoing itself (which is best-effort). * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Database/V1/UpdateDatabaseRequest.php b/Spanner/src/Admin/Database/V1/UpdateDatabaseRequest.php index 27e65dd2d9c8..7fecddf2dd12 100644 --- a/Spanner/src/Admin/Database/V1/UpdateDatabaseRequest.php +++ b/Spanner/src/Admin/Database/V1/UpdateDatabaseRequest.php @@ -23,14 +23,14 @@ class UpdateDatabaseRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.database.v1.Database database = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $database = null; + protected $database = null; /** * Required. The list of fields to update. Currently, only * `enable_drop_protection` field can be updated. * * Generated from protobuf field .google.protobuf.FieldMask update_mask = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $update_mask = null; + protected $update_mask = null; /** * @param \Google\Cloud\Spanner\Admin\Database\V1\Database $database Required. The database to update. diff --git a/Spanner/src/Admin/Instance/V1/AutoscalingConfig.php b/Spanner/src/Admin/Instance/V1/AutoscalingConfig.php index 341c101be0f1..0553c0701882 100644 --- a/Spanner/src/Admin/Instance/V1/AutoscalingConfig.php +++ b/Spanner/src/Admin/Instance/V1/AutoscalingConfig.php @@ -20,7 +20,7 @@ class AutoscalingConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.AutoscalingConfig.AutoscalingLimits autoscaling_limits = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $autoscaling_limits = null; + protected $autoscaling_limits = null; /** * Required. The autoscaling targets for an instance. * diff --git a/Spanner/src/Admin/Instance/V1/AutoscalingConfig/AutoscalingTargets.php b/Spanner/src/Admin/Instance/V1/AutoscalingConfig/AutoscalingTargets.php index 45f359fee624..a4325501a92d 100644 --- a/Spanner/src/Admin/Instance/V1/AutoscalingConfig/AutoscalingTargets.php +++ b/Spanner/src/Admin/Instance/V1/AutoscalingConfig/AutoscalingTargets.php @@ -23,7 +23,7 @@ class AutoscalingTargets extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 high_priority_cpu_utilization_percent = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $high_priority_cpu_utilization_percent = 0; + protected $high_priority_cpu_utilization_percent = 0; /** * Required. The target storage utilization percentage that the autoscaler * should be trying to achieve for the instance. This number is on a scale @@ -32,7 +32,7 @@ class AutoscalingTargets extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 storage_utilization_percent = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $storage_utilization_percent = 0; + protected $storage_utilization_percent = 0; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/Client/InstanceAdminClient.php b/Spanner/src/Admin/Instance/V1/Client/InstanceAdminClient.php index 1b3d5d0816e3..5203711d0482 100644 --- a/Spanner/src/Admin/Instance/V1/Client/InstanceAdminClient.php +++ b/Spanner/src/Admin/Instance/V1/Client/InstanceAdminClient.php @@ -1,6 +1,6 @@ .google.spanner.admin.instance.v1.InstanceConfig instance_config = 1; */ - private $instance_config = null; + protected $instance_config = null; /** * The progress of the * [CreateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstanceConfig] @@ -29,13 +29,13 @@ class CreateInstanceConfigMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.OperationProgress progress = 2; */ - private $progress = null; + protected $progress = null; /** * The time at which this operation was cancelled. * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/CreateInstanceConfigRequest.php b/Spanner/src/Admin/Instance/V1/CreateInstanceConfigRequest.php index 72182a6d1a46..8044c33e1cf9 100644 --- a/Spanner/src/Admin/Instance/V1/CreateInstanceConfigRequest.php +++ b/Spanner/src/Admin/Instance/V1/CreateInstanceConfigRequest.php @@ -22,7 +22,7 @@ class CreateInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The ID of the instance configuration to create. Valid identifiers * are of the form `custom-[-a-z0-9]*[a-z0-9]` and must be between 2 and 64 @@ -31,7 +31,7 @@ class CreateInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string instance_config_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_config_id = ''; + protected $instance_config_id = ''; /** * Required. The InstanceConfig proto of the configuration to create. * instance_config.name must be @@ -41,14 +41,14 @@ class CreateInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstanceConfig instance_config = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_config = null; + protected $instance_config = null; /** * An option to validate, but not actually execute, a request, * and provide the same response. * * Generated from protobuf field bool validate_only = 4; */ - private $validate_only = false; + protected $validate_only = false; /** * @param string $parent Required. The name of the project in which to create the instance diff --git a/Spanner/src/Admin/Instance/V1/CreateInstanceMetadata.php b/Spanner/src/Admin/Instance/V1/CreateInstanceMetadata.php index 42d25b24f565..0b08f1253a6f 100644 --- a/Spanner/src/Admin/Instance/V1/CreateInstanceMetadata.php +++ b/Spanner/src/Admin/Instance/V1/CreateInstanceMetadata.php @@ -21,7 +21,7 @@ class CreateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.Instance instance = 1; */ - private $instance = null; + protected $instance = null; /** * The time at which the * [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance] @@ -29,7 +29,7 @@ class CreateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * The time at which this operation was cancelled. If set, this operation is * in the process of undoing itself (which is guaranteed to succeed) and @@ -37,19 +37,19 @@ class CreateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * The time at which this operation failed or was completed successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 4; */ - private $end_time = null; + protected $end_time = null; /** * The expected fulfillment period of this create operation. * * Generated from protobuf field .google.spanner.admin.instance.v1.FulfillmentPeriod expected_fulfillment_period = 5; */ - private $expected_fulfillment_period = 0; + protected $expected_fulfillment_period = 0; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/CreateInstancePartitionMetadata.php b/Spanner/src/Admin/Instance/V1/CreateInstancePartitionMetadata.php index 5829af2605bf..3494bbcea33a 100644 --- a/Spanner/src/Admin/Instance/V1/CreateInstancePartitionMetadata.php +++ b/Spanner/src/Admin/Instance/V1/CreateInstancePartitionMetadata.php @@ -21,7 +21,7 @@ class CreateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstancePartition instance_partition = 1; */ - private $instance_partition = null; + protected $instance_partition = null; /** * The time at which the * [CreateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstancePartition] @@ -29,7 +29,7 @@ class CreateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * The time at which this operation was cancelled. If set, this operation is * in the process of undoing itself (which is guaranteed to succeed) and @@ -37,13 +37,13 @@ class CreateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * The time at which this operation failed or was completed successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 4; */ - private $end_time = null; + protected $end_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/CreateInstancePartitionRequest.php b/Spanner/src/Admin/Instance/V1/CreateInstancePartitionRequest.php index 7cf1add96d36..d12f2223ea98 100644 --- a/Spanner/src/Admin/Instance/V1/CreateInstancePartitionRequest.php +++ b/Spanner/src/Admin/Instance/V1/CreateInstancePartitionRequest.php @@ -23,7 +23,7 @@ class CreateInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The ID of the instance partition to create. Valid identifiers are * of the form `[a-z][-a-z0-9]*[a-z0-9]` and must be between 2 and 64 @@ -31,7 +31,7 @@ class CreateInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string instance_partition_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_partition_id = ''; + protected $instance_partition_id = ''; /** * Required. The instance partition to create. The instance_partition.name may * be omitted, but if specified must be @@ -39,7 +39,7 @@ class CreateInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstancePartition instance_partition = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_partition = null; + protected $instance_partition = null; /** * @param string $parent Required. The name of the instance in which to create the instance diff --git a/Spanner/src/Admin/Instance/V1/CreateInstanceRequest.php b/Spanner/src/Admin/Instance/V1/CreateInstanceRequest.php index c31fbd9c0f15..1954b4933248 100644 --- a/Spanner/src/Admin/Instance/V1/CreateInstanceRequest.php +++ b/Spanner/src/Admin/Instance/V1/CreateInstanceRequest.php @@ -22,7 +22,7 @@ class CreateInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Required. The ID of the instance to create. Valid identifiers are of the * form `[a-z][-a-z0-9]*[a-z0-9]` and must be between 2 and 64 characters in @@ -30,14 +30,14 @@ class CreateInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string instance_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_id = ''; + protected $instance_id = ''; /** * Required. The instance to create. The name may be omitted, but if * specified must be `/instances/`. * * Generated from protobuf field .google.spanner.admin.instance.v1.Instance instance = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance = null; + protected $instance = null; /** * @param string $parent Required. The name of the project in which to create the instance. Values diff --git a/Spanner/src/Admin/Instance/V1/DeleteInstanceConfigRequest.php b/Spanner/src/Admin/Instance/V1/DeleteInstanceConfigRequest.php index 6cc41a917dcc..ad65aa6051e0 100644 --- a/Spanner/src/Admin/Instance/V1/DeleteInstanceConfigRequest.php +++ b/Spanner/src/Admin/Instance/V1/DeleteInstanceConfigRequest.php @@ -23,7 +23,7 @@ class DeleteInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * Used for optimistic concurrency control as a way to help prevent * simultaneous deletes of an instance configuration from overwriting each @@ -35,14 +35,14 @@ class DeleteInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string etag = 2; */ - private $etag = ''; + protected $etag = ''; /** * An option to validate, but not actually execute, a request, * and provide the same response. * * Generated from protobuf field bool validate_only = 3; */ - private $validate_only = false; + protected $validate_only = false; /** * @param string $name Required. The name of the instance configuration to be deleted. diff --git a/Spanner/src/Admin/Instance/V1/DeleteInstancePartitionRequest.php b/Spanner/src/Admin/Instance/V1/DeleteInstancePartitionRequest.php index 74984498840b..430f00f61956 100644 --- a/Spanner/src/Admin/Instance/V1/DeleteInstancePartitionRequest.php +++ b/Spanner/src/Admin/Instance/V1/DeleteInstancePartitionRequest.php @@ -23,7 +23,7 @@ class DeleteInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * Optional. If not empty, the API only deletes the instance partition when * the etag provided matches the current status of the requested instance @@ -32,7 +32,7 @@ class DeleteInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string etag = 2; */ - private $etag = ''; + protected $etag = ''; /** * @param string $name Required. The name of the instance partition to be deleted. diff --git a/Spanner/src/Admin/Instance/V1/DeleteInstanceRequest.php b/Spanner/src/Admin/Instance/V1/DeleteInstanceRequest.php index ac15b862152d..f8633ecef64d 100644 --- a/Spanner/src/Admin/Instance/V1/DeleteInstanceRequest.php +++ b/Spanner/src/Admin/Instance/V1/DeleteInstanceRequest.php @@ -22,7 +22,7 @@ class DeleteInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the instance to be deleted. Values are of the form diff --git a/Spanner/src/Admin/Instance/V1/GetInstanceConfigRequest.php b/Spanner/src/Admin/Instance/V1/GetInstanceConfigRequest.php index d245b4f87aa5..c0d299ac0b80 100644 --- a/Spanner/src/Admin/Instance/V1/GetInstanceConfigRequest.php +++ b/Spanner/src/Admin/Instance/V1/GetInstanceConfigRequest.php @@ -22,7 +22,7 @@ class GetInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the requested instance configuration. Values are of diff --git a/Spanner/src/Admin/Instance/V1/GetInstancePartitionRequest.php b/Spanner/src/Admin/Instance/V1/GetInstancePartitionRequest.php index 960eefa5e295..e703503feb23 100644 --- a/Spanner/src/Admin/Instance/V1/GetInstancePartitionRequest.php +++ b/Spanner/src/Admin/Instance/V1/GetInstancePartitionRequest.php @@ -23,7 +23,7 @@ class GetInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the requested instance partition. Values are of diff --git a/Spanner/src/Admin/Instance/V1/GetInstanceRequest.php b/Spanner/src/Admin/Instance/V1/GetInstanceRequest.php index d16760cd5c65..6f054759ba44 100644 --- a/Spanner/src/Admin/Instance/V1/GetInstanceRequest.php +++ b/Spanner/src/Admin/Instance/V1/GetInstanceRequest.php @@ -22,7 +22,7 @@ class GetInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * If field_mask is present, specifies the subset of * [Instance][google.spanner.admin.instance.v1.Instance] fields that should be @@ -31,7 +31,7 @@ class GetInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.FieldMask field_mask = 2; */ - private $field_mask = null; + protected $field_mask = null; /** * @param string $name Required. The name of the requested instance. Values are of the form diff --git a/Spanner/src/Admin/Instance/V1/Instance.php b/Spanner/src/Admin/Instance/V1/Instance.php index 27cafab2cc74..04ec9c6386d7 100644 --- a/Spanner/src/Admin/Instance/V1/Instance.php +++ b/Spanner/src/Admin/Instance/V1/Instance.php @@ -23,7 +23,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $name = ''; + protected $name = ''; /** * Required. The name of the instance's configuration. Values are of the form * `projects//instanceConfigs/`. See @@ -32,14 +32,14 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string config = 2 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $config = ''; + protected $config = ''; /** * Required. The descriptive name for this instance as it appears in UIs. * Must be unique per project and between 4 and 30 characters in length. * * Generated from protobuf field string display_name = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $display_name = ''; + protected $display_name = ''; /** * The number of nodes allocated to this instance. At most, one of either * `node_count` or `processing_units` should be present in the message. @@ -58,7 +58,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 node_count = 5; */ - private $node_count = 0; + protected $node_count = 0; /** * The number of processing units allocated to this instance. At most, one of * either `processing_units` or `node_count` should be present in the message. @@ -79,7 +79,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 processing_units = 9; */ - private $processing_units = 0; + protected $processing_units = 0; /** * Output only. Lists the compute capacity per ReplicaSelection. A replica * selection identifies a set of replicas with common properties. Replicas @@ -96,7 +96,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.AutoscalingConfig autoscaling_config = 17 [(.google.api.field_behavior) = OPTIONAL]; */ - private $autoscaling_config = null; + protected $autoscaling_config = null; /** * Output only. The current instance state. For * [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance], @@ -106,7 +106,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.Instance.State state = 6 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $state = 0; + protected $state = 0; /** * Cloud Labels are a flexible and lightweight mechanism for organizing cloud * resources into groups that reflect a customer's organizational needs and @@ -141,7 +141,7 @@ class Instance extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp create_time = 11 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $create_time = null; + protected $create_time = null; /** * Output only. The time at which the instance was most recently updated. * diff --git a/Spanner/src/Admin/Instance/V1/InstanceAdminGrpcClient.php b/Spanner/src/Admin/Instance/V1/InstanceAdminGrpcClient.php deleted file mode 100644 index aa03f20e91b5..000000000000 --- a/Spanner/src/Admin/Instance/V1/InstanceAdminGrpcClient.php +++ /dev/null @@ -1,460 +0,0 @@ -_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/ListInstanceConfigs', - $argument, - ['\Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsResponse', 'decode'], - $metadata, $options); - } - - /** - * Gets information about a particular instance configuration. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetInstanceConfig(\Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/GetInstanceConfig', - $argument, - ['\Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig', 'decode'], - $metadata, $options); - } - - /** - * Creates an instance config and begins preparing it to be used. The - * returned [long-running operation][google.longrunning.Operation] - * can be used to track the progress of preparing the new - * instance config. The instance config name is assigned by the caller. If the - * named instance config already exists, `CreateInstanceConfig` returns - * `ALREADY_EXISTS`. - * - * Immediately after the request returns: - * - * * The instance config is readable via the API, with all requested - * attributes. The instance config's - * [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] - * field is set to true. Its state is `CREATING`. - * - * While the operation is pending: - * - * * Cancelling the operation renders the instance config immediately - * unreadable via the API. - * * Except for deleting the creating resource, all other attempts to modify - * the instance config are rejected. - * - * Upon completion of the returned operation: - * - * * Instances can be created using the instance configuration. - * * The instance config's - * [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] - * field becomes false. Its state becomes `READY`. - * - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format - * `/operations/` and can be used to track - * creation of the instance config. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [CreateInstanceConfigMetadata][google.spanner.admin.instance.v1.CreateInstanceConfigMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig], if - * successful. - * - * Authorization requires `spanner.instanceConfigs.create` permission on - * the resource - * [parent][google.spanner.admin.instance.v1.CreateInstanceConfigRequest.parent]. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function CreateInstanceConfig(\Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/CreateInstanceConfig', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Updates an instance config. The returned - * [long-running operation][google.longrunning.Operation] can be used to track - * the progress of updating the instance. If the named instance config does - * not exist, returns `NOT_FOUND`. - * - * Only user managed configurations can be updated. - * - * Immediately after the request returns: - * - * * The instance config's - * [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] - * field is set to true. - * - * While the operation is pending: - * - * * Cancelling the operation sets its metadata's - * [cancel_time][google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata.cancel_time]. - * The operation is guaranteed to succeed at undoing all changes, after - * which point it terminates with a `CANCELLED` status. - * * All other attempts to modify the instance config are rejected. - * * Reading the instance config via the API continues to give the - * pre-request values. - * - * Upon completion of the returned operation: - * - * * Creating instances using the instance configuration uses the new - * values. - * * The instance config's new values are readable via the API. - * * The instance config's - * [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] - * field becomes false. - * - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format - * `/operations/` and can be used to track - * the instance config modification. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [UpdateInstanceConfigMetadata][google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig], if - * successful. - * - * Authorization requires `spanner.instanceConfigs.update` permission on - * the resource [name][google.spanner.admin.instance.v1.InstanceConfig.name]. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function UpdateInstanceConfig(\Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/UpdateInstanceConfig', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Deletes the instance config. Deletion is only allowed when no - * instances are using the configuration. If any instances are using - * the config, returns `FAILED_PRECONDITION`. - * - * Only user managed configurations can be deleted. - * - * Authorization requires `spanner.instanceConfigs.delete` permission on - * the resource [name][google.spanner.admin.instance.v1.InstanceConfig.name]. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function DeleteInstanceConfig(\Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/DeleteInstanceConfig', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Lists the user-managed instance config [long-running - * operations][google.longrunning.Operation] in the given project. An instance - * config operation has a name of the form - * `projects//instanceConfigs//operations/`. - * The long-running operation - * [metadata][google.longrunning.Operation.metadata] field type - * `metadata.type_url` describes the type of the metadata. Operations returned - * include those that have completed/failed/canceled within the last 7 days, - * and pending operations. Operations returned are ordered by - * `operation.metadata.value.start_time` in descending order starting - * from the most recently started operation. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigOperationsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListInstanceConfigOperations(\Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigOperationsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/ListInstanceConfigOperations', - $argument, - ['\Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigOperationsResponse', 'decode'], - $metadata, $options); - } - - /** - * Lists all instances in the given project. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListInstances(\Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/ListInstances', - $argument, - ['\Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesResponse', 'decode'], - $metadata, $options); - } - - /** - * Gets information about a particular instance. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetInstance(\Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/GetInstance', - $argument, - ['\Google\Cloud\Spanner\Admin\Instance\V1\Instance', 'decode'], - $metadata, $options); - } - - /** - * Creates an instance and begins preparing it to begin serving. The - * returned [long-running operation][google.longrunning.Operation] - * can be used to track the progress of preparing the new - * instance. The instance name is assigned by the caller. If the - * named instance already exists, `CreateInstance` returns - * `ALREADY_EXISTS`. - * - * Immediately upon completion of this request: - * - * * The instance is readable via the API, with all requested attributes - * but no allocated resources. Its state is `CREATING`. - * - * Until completion of the returned operation: - * - * * Cancelling the operation renders the instance immediately unreadable - * via the API. - * * The instance can be deleted. - * * All other attempts to modify the instance are rejected. - * - * Upon completion of the returned operation: - * - * * Billing for all successfully-allocated resources begins (some types - * may have lower than the requested levels). - * * Databases can be created in the instance. - * * The instance's allocated resource levels are readable via the API. - * * The instance's state becomes `READY`. - * - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format `/operations/` and - * can be used to track creation of the instance. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [CreateInstanceMetadata][google.spanner.admin.instance.v1.CreateInstanceMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [Instance][google.spanner.admin.instance.v1.Instance], if successful. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function CreateInstance(\Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/CreateInstance', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Updates an instance, and begins allocating or releasing resources - * as requested. The returned [long-running - * operation][google.longrunning.Operation] can be used to track the - * progress of updating the instance. If the named instance does not - * exist, returns `NOT_FOUND`. - * - * Immediately upon completion of this request: - * - * * For resource types for which a decrease in the instance's allocation - * has been requested, billing is based on the newly-requested level. - * - * Until completion of the returned operation: - * - * * Cancelling the operation sets its metadata's - * [cancel_time][google.spanner.admin.instance.v1.UpdateInstanceMetadata.cancel_time], - * and begins restoring resources to their pre-request values. The - * operation is guaranteed to succeed at undoing all resource changes, - * after which point it terminates with a `CANCELLED` status. - * * All other attempts to modify the instance are rejected. - * * Reading the instance via the API continues to give the pre-request - * resource levels. - * - * Upon completion of the returned operation: - * - * * Billing begins for all successfully-allocated resources (some types - * may have lower than the requested levels). - * * All newly-reserved resources are available for serving the instance's - * tables. - * * The instance's new resource levels are readable via the API. - * - * The returned [long-running operation][google.longrunning.Operation] will - * have a name of the format `/operations/` and - * can be used to track the instance modification. The - * [metadata][google.longrunning.Operation.metadata] field type is - * [UpdateInstanceMetadata][google.spanner.admin.instance.v1.UpdateInstanceMetadata]. - * The [response][google.longrunning.Operation.response] field type is - * [Instance][google.spanner.admin.instance.v1.Instance], if successful. - * - * Authorization requires `spanner.instances.update` permission on - * the resource [name][google.spanner.admin.instance.v1.Instance.name]. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function UpdateInstance(\Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/UpdateInstance', - $argument, - ['\Google\LongRunning\Operation', 'decode'], - $metadata, $options); - } - - /** - * Deletes an instance. - * - * Immediately upon completion of the request: - * - * * Billing ceases for all of the instance's reserved resources. - * - * Soon afterward: - * - * * The instance and *all of its databases* immediately and - * irrevocably disappear from the API. All data in the databases - * is permanently deleted. - * @param \Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function DeleteInstance(\Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/DeleteInstance', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Sets the access control policy on an instance resource. Replaces any - * existing policy. - * - * Authorization requires `spanner.instances.setIamPolicy` on - * [resource][google.iam.v1.SetIamPolicyRequest.resource]. - * @param \Google\Cloud\Iam\V1\SetIamPolicyRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function SetIamPolicy(\Google\Cloud\Iam\V1\SetIamPolicyRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/SetIamPolicy', - $argument, - ['\Google\Cloud\Iam\V1\Policy', 'decode'], - $metadata, $options); - } - - /** - * Gets the access control policy for an instance resource. Returns an empty - * policy if an instance exists but does not have a policy set. - * - * Authorization requires `spanner.instances.getIamPolicy` on - * [resource][google.iam.v1.GetIamPolicyRequest.resource]. - * @param \Google\Cloud\Iam\V1\GetIamPolicyRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetIamPolicy(\Google\Cloud\Iam\V1\GetIamPolicyRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/GetIamPolicy', - $argument, - ['\Google\Cloud\Iam\V1\Policy', 'decode'], - $metadata, $options); - } - - /** - * Returns permissions that the caller has on the specified instance resource. - * - * Attempting this RPC on a non-existent Cloud Spanner instance resource will - * result in a NOT_FOUND error if the user has `spanner.instances.list` - * permission on the containing Google Cloud Project. Otherwise returns an - * empty set of permissions. - * @param \Google\Cloud\Iam\V1\TestIamPermissionsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function TestIamPermissions(\Google\Cloud\Iam\V1\TestIamPermissionsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.admin.instance.v1.InstanceAdmin/TestIamPermissions', - $argument, - ['\Google\Cloud\Iam\V1\TestIamPermissionsResponse', 'decode'], - $metadata, $options); - } - -} diff --git a/Spanner/src/Admin/Instance/V1/InstanceConfig.php b/Spanner/src/Admin/Instance/V1/InstanceConfig.php index 73b4e0350fee..8335fa12e998 100644 --- a/Spanner/src/Admin/Instance/V1/InstanceConfig.php +++ b/Spanner/src/Admin/Instance/V1/InstanceConfig.php @@ -24,20 +24,20 @@ class InstanceConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1; */ - private $name = ''; + protected $name = ''; /** * The name of this instance configuration as it appears in UIs. * * Generated from protobuf field string display_name = 2; */ - private $display_name = ''; + protected $display_name = ''; /** * Output only. Whether this instance configuration is a Google-managed or * user-managed configuration. * * Generated from protobuf field .google.spanner.admin.instance.v1.InstanceConfig.Type config_type = 5 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $config_type = 0; + protected $config_type = 0; /** * The geographic placement of nodes in this instance configuration and their * replication properties. @@ -60,7 +60,7 @@ class InstanceConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string base_config = 7 [(.google.api.resource_reference) = { */ - private $base_config = ''; + protected $base_config = ''; /** * Cloud Labels are a flexible and lightweight mechanism for organizing cloud * resources into groups that reflect a customer's organizational needs and @@ -99,7 +99,7 @@ class InstanceConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string etag = 9; */ - private $etag = ''; + protected $etag = ''; /** * Allowed values of the "default_leader" schema option for databases in * instances that use this instance configuration. @@ -114,14 +114,14 @@ class InstanceConfig extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool reconciling = 10 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $reconciling = false; + protected $reconciling = false; /** * Output only. The current instance configuration state. Applicable only for * `USER_MANAGED` configurations. * * Generated from protobuf field .google.spanner.admin.instance.v1.InstanceConfig.State state = 11 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $state = 0; + protected $state = 0; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/InstanceConfig_State.php b/Spanner/src/Admin/Instance/V1/InstanceConfig_State.php deleted file mode 100644 index e881bb0b827e..000000000000 --- a/Spanner/src/Admin/Instance/V1/InstanceConfig_State.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $name = ''; + protected $name = ''; /** * Required. The name of the instance partition's configuration. Values are of * the form `projects//instanceConfigs/`. See also @@ -35,33 +35,33 @@ class InstancePartition extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string config = 2 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $config = ''; + protected $config = ''; /** * Required. The descriptive name for this instance partition as it appears in * UIs. Must be unique per project and between 4 and 30 characters in length. * * Generated from protobuf field string display_name = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $display_name = ''; + protected $display_name = ''; /** * Output only. The current instance partition state. * * Generated from protobuf field .google.spanner.admin.instance.v1.InstancePartition.State state = 7 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $state = 0; + protected $state = 0; /** * Output only. The time at which the instance partition was created. * * Generated from protobuf field .google.protobuf.Timestamp create_time = 8 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $create_time = null; + protected $create_time = null; /** * Output only. The time at which the instance partition was most recently * updated. * * Generated from protobuf field .google.protobuf.Timestamp update_time = 9 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $update_time = null; + protected $update_time = null; /** * Output only. The names of the databases that reference this * instance partition. Referencing databases should share the parent instance. @@ -94,7 +94,7 @@ class InstancePartition extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string etag = 12; */ - private $etag = ''; + protected $etag = ''; protected $compute_capacity; /** diff --git a/Spanner/src/Admin/Instance/V1/Instance_State.php b/Spanner/src/Admin/Instance/V1/Instance_State.php deleted file mode 100644 index b53e838f3a22..000000000000 --- a/Spanner/src/Admin/Instance/V1/Instance_State.php +++ /dev/null @@ -1,16 +0,0 @@ -string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * An expression that filters the list of returned operations. * A filter expression consists of a field name, a @@ -64,14 +64,14 @@ class ListInstanceConfigOperationsRequest extends \Google\Protobuf\Internal\Mess * * Generated from protobuf field string filter = 2; */ - private $filter = ''; + protected $filter = ''; /** * Number of operations to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.instance.v1.ListInstanceConfigOperationsResponse.next_page_token] @@ -81,7 +81,7 @@ class ListInstanceConfigOperationsRequest extends \Google\Protobuf\Internal\Mess * * Generated from protobuf field string page_token = 4; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The project of the instance configuration operations. diff --git a/Spanner/src/Admin/Instance/V1/ListInstanceConfigOperationsResponse.php b/Spanner/src/Admin/Instance/V1/ListInstanceConfigOperationsResponse.php index 10d805eb3e32..7f1b94df9792 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstanceConfigOperationsResponse.php +++ b/Spanner/src/Admin/Instance/V1/ListInstanceConfigOperationsResponse.php @@ -33,7 +33,7 @@ class ListInstanceConfigOperationsResponse extends \Google\Protobuf\Internal\Mes * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/ListInstanceConfigsRequest.php b/Spanner/src/Admin/Instance/V1/ListInstanceConfigsRequest.php index dbcafba1d48a..8a0b73a6192b 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstanceConfigsRequest.php +++ b/Spanner/src/Admin/Instance/V1/ListInstanceConfigsRequest.php @@ -23,14 +23,14 @@ class ListInstanceConfigsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Number of instance configurations to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 2; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.instance.v1.ListInstanceConfigsResponse.next_page_token] @@ -39,7 +39,7 @@ class ListInstanceConfigsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 3; */ - private $page_token = ''; + protected $page_token = ''; /** * @param string $parent Required. The name of the project for which a list of supported instance diff --git a/Spanner/src/Admin/Instance/V1/ListInstanceConfigsResponse.php b/Spanner/src/Admin/Instance/V1/ListInstanceConfigsResponse.php index 6c94a0bd4e28..73241c4da20d 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstanceConfigsResponse.php +++ b/Spanner/src/Admin/Instance/V1/ListInstanceConfigsResponse.php @@ -29,7 +29,7 @@ class ListInstanceConfigsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsRequest.php b/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsRequest.php index 3f66a1028928..a5dfe05b950b 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsRequest.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsRequest.php @@ -22,7 +22,7 @@ class ListInstancePartitionOperationsRequest extends \Google\Protobuf\Internal\M * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Optional. An expression that filters the list of returned operations. * A filter expression consists of a field name, a @@ -64,14 +64,14 @@ class ListInstancePartitionOperationsRequest extends \Google\Protobuf\Internal\M * * Generated from protobuf field string filter = 2 [(.google.api.field_behavior) = OPTIONAL]; */ - private $filter = ''; + protected $filter = ''; /** * Optional. Number of operations to be returned in the response. If 0 or * less, defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 3 [(.google.api.field_behavior) = OPTIONAL]; */ - private $page_size = 0; + protected $page_size = 0; /** * Optional. If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.instance.v1.ListInstancePartitionOperationsResponse.next_page_token] @@ -81,7 +81,7 @@ class ListInstancePartitionOperationsRequest extends \Google\Protobuf\Internal\M * * Generated from protobuf field string page_token = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $page_token = ''; + protected $page_token = ''; /** * Optional. Deadline used while retrieving metadata for instance partition * operations. Instance partitions whose operation metadata cannot be @@ -91,7 +91,7 @@ class ListInstancePartitionOperationsRequest extends \Google\Protobuf\Internal\M * * Generated from protobuf field .google.protobuf.Timestamp instance_partition_deadline = 5 [(.google.api.field_behavior) = OPTIONAL]; */ - private $instance_partition_deadline = null; + protected $instance_partition_deadline = null; /** * @param string $parent Required. The parent instance of the instance partition operations. diff --git a/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsResponse.php b/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsResponse.php index 8ed281a554bf..44acc94c32aa 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsResponse.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancePartitionOperationsResponse.php @@ -33,7 +33,7 @@ class ListInstancePartitionOperationsResponse extends \Google\Protobuf\Internal\ * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * The list of unreachable instance partitions. * It includes the names of instance partitions whose operation metadata could diff --git a/Spanner/src/Admin/Instance/V1/ListInstancePartitionsRequest.php b/Spanner/src/Admin/Instance/V1/ListInstancePartitionsRequest.php index 877be2da98d1..aa607b0405c0 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancePartitionsRequest.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancePartitionsRequest.php @@ -22,14 +22,14 @@ class ListInstancePartitionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Number of instance partitions to be returned in the response. If 0 or less, * defaults to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 2; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.instance.v1.ListInstancePartitionsResponse.next_page_token] @@ -38,7 +38,7 @@ class ListInstancePartitionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 3; */ - private $page_token = ''; + protected $page_token = ''; /** * Optional. Deadline used while retrieving metadata for instance partitions. * Instance partitions whose metadata cannot be retrieved within this deadline @@ -49,7 +49,7 @@ class ListInstancePartitionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp instance_partition_deadline = 4 [(.google.api.field_behavior) = OPTIONAL]; */ - private $instance_partition_deadline = null; + protected $instance_partition_deadline = null; /** * @param string $parent Required. The instance whose instance partitions should be listed. Values diff --git a/Spanner/src/Admin/Instance/V1/ListInstancePartitionsResponse.php b/Spanner/src/Admin/Instance/V1/ListInstancePartitionsResponse.php index 916ce4a772ff..17a9ccba92b2 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancePartitionsResponse.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancePartitionsResponse.php @@ -29,7 +29,7 @@ class ListInstancePartitionsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * The list of unreachable instance partitions. * It includes the names of instance partitions whose metadata could diff --git a/Spanner/src/Admin/Instance/V1/ListInstancesRequest.php b/Spanner/src/Admin/Instance/V1/ListInstancesRequest.php index 4c551b16fec6..acf67c1d3534 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancesRequest.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancesRequest.php @@ -22,14 +22,14 @@ class ListInstancesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string parent = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $parent = ''; + protected $parent = ''; /** * Number of instances to be returned in the response. If 0 or less, defaults * to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 2; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.admin.instance.v1.ListInstancesResponse.next_page_token] @@ -38,7 +38,7 @@ class ListInstancesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 3; */ - private $page_token = ''; + protected $page_token = ''; /** * An expression for filtering the results of the request. Filter rules are * case insensitive. The fields eligible for filtering are: @@ -59,7 +59,7 @@ class ListInstancesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string filter = 4; */ - private $filter = ''; + protected $filter = ''; /** * Deadline used while retrieving metadata for instances. * Instances whose metadata cannot be retrieved within this deadline will be @@ -70,7 +70,7 @@ class ListInstancesRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp instance_deadline = 5; */ - private $instance_deadline = null; + protected $instance_deadline = null; /** * @param string $parent Required. The name of the project for which a list of instances is diff --git a/Spanner/src/Admin/Instance/V1/ListInstancesResponse.php b/Spanner/src/Admin/Instance/V1/ListInstancesResponse.php index 238ee34febe6..cc134da2aac1 100644 --- a/Spanner/src/Admin/Instance/V1/ListInstancesResponse.php +++ b/Spanner/src/Admin/Instance/V1/ListInstancesResponse.php @@ -29,7 +29,7 @@ class ListInstancesResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * The list of unreachable instances. * It includes the names of instances whose metadata could not be retrieved diff --git a/Spanner/src/Admin/Instance/V1/OperationProgress.php b/Spanner/src/Admin/Instance/V1/OperationProgress.php index c0e737b86467..e6b2e6c751d6 100644 --- a/Spanner/src/Admin/Instance/V1/OperationProgress.php +++ b/Spanner/src/Admin/Instance/V1/OperationProgress.php @@ -22,20 +22,20 @@ class OperationProgress extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 progress_percent = 1; */ - private $progress_percent = 0; + protected $progress_percent = 0; /** * Time the request was received. * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * If set, the time at which this operation failed or was completed * successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 3; */ - private $end_time = null; + protected $end_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/ReplicaInfo.php b/Spanner/src/Admin/Instance/V1/ReplicaInfo.php index 858d005c5bc3..634aae3a12c4 100644 --- a/Spanner/src/Admin/Instance/V1/ReplicaInfo.php +++ b/Spanner/src/Admin/Instance/V1/ReplicaInfo.php @@ -18,13 +18,13 @@ class ReplicaInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string location = 1; */ - private $location = ''; + protected $location = ''; /** * The type of replica. * * Generated from protobuf field .google.spanner.admin.instance.v1.ReplicaInfo.ReplicaType type = 2; */ - private $type = 0; + protected $type = 0; /** * If true, this location is designated as the default leader location where * leader replicas are placed. See the [region types @@ -33,7 +33,7 @@ class ReplicaInfo extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool default_leader_location = 3; */ - private $default_leader_location = false; + protected $default_leader_location = false; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/ReplicaInfo_ReplicaType.php b/Spanner/src/Admin/Instance/V1/ReplicaInfo_ReplicaType.php deleted file mode 100644 index 05b3e6d0daed..000000000000 --- a/Spanner/src/Admin/Instance/V1/ReplicaInfo_ReplicaType.php +++ /dev/null @@ -1,16 +0,0 @@ -.google.spanner.admin.instance.v1.InstanceConfig instance_config = 1; */ - private $instance_config = null; + protected $instance_config = null; /** * The progress of the * [UpdateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstanceConfig] @@ -29,13 +29,13 @@ class UpdateInstanceConfigMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.OperationProgress progress = 2; */ - private $progress = null; + protected $progress = null; /** * The time at which this operation was cancelled. * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/UpdateInstanceConfigRequest.php b/Spanner/src/Admin/Instance/V1/UpdateInstanceConfigRequest.php index 29962464c2b5..799fc558ceaa 100644 --- a/Spanner/src/Admin/Instance/V1/UpdateInstanceConfigRequest.php +++ b/Spanner/src/Admin/Instance/V1/UpdateInstanceConfigRequest.php @@ -27,7 +27,7 @@ class UpdateInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstanceConfig instance_config = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_config = null; + protected $instance_config = null; /** * Required. A mask specifying which fields in * [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] should be @@ -38,14 +38,14 @@ class UpdateInstanceConfigRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.FieldMask update_mask = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $update_mask = null; + protected $update_mask = null; /** * An option to validate, but not actually execute, a request, * and provide the same response. * * Generated from protobuf field bool validate_only = 3; */ - private $validate_only = false; + protected $validate_only = false; /** * @param \Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig $instanceConfig Required. The user instance configuration to update, which must always diff --git a/Spanner/src/Admin/Instance/V1/UpdateInstanceMetadata.php b/Spanner/src/Admin/Instance/V1/UpdateInstanceMetadata.php index a679660b4d0f..75231230c5bc 100644 --- a/Spanner/src/Admin/Instance/V1/UpdateInstanceMetadata.php +++ b/Spanner/src/Admin/Instance/V1/UpdateInstanceMetadata.php @@ -21,7 +21,7 @@ class UpdateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.Instance instance = 1; */ - private $instance = null; + protected $instance = null; /** * The time at which * [UpdateInstance][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance] @@ -29,7 +29,7 @@ class UpdateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * The time at which this operation was cancelled. If set, this operation is * in the process of undoing itself (which is guaranteed to succeed) and @@ -37,19 +37,19 @@ class UpdateInstanceMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * The time at which this operation failed or was completed successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 4; */ - private $end_time = null; + protected $end_time = null; /** * The expected fulfillment period of this update operation. * * Generated from protobuf field .google.spanner.admin.instance.v1.FulfillmentPeriod expected_fulfillment_period = 5; */ - private $expected_fulfillment_period = 0; + protected $expected_fulfillment_period = 0; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionMetadata.php b/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionMetadata.php index f136c262b2de..7d36e9dc23bd 100644 --- a/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionMetadata.php +++ b/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionMetadata.php @@ -21,7 +21,7 @@ class UpdateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstancePartition instance_partition = 1; */ - private $instance_partition = null; + protected $instance_partition = null; /** * The time at which * [UpdateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstancePartition] @@ -29,7 +29,7 @@ class UpdateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp start_time = 2; */ - private $start_time = null; + protected $start_time = null; /** * The time at which this operation was cancelled. If set, this operation is * in the process of undoing itself (which is guaranteed to succeed) and @@ -37,13 +37,13 @@ class UpdateInstancePartitionMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp cancel_time = 3; */ - private $cancel_time = null; + protected $cancel_time = null; /** * The time at which this operation failed or was completed successfully. * * Generated from protobuf field .google.protobuf.Timestamp end_time = 4; */ - private $end_time = null; + protected $end_time = null; /** * Constructor. diff --git a/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionRequest.php b/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionRequest.php index e58ea51519e3..8cab2a4ee3fa 100644 --- a/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionRequest.php +++ b/Spanner/src/Admin/Instance/V1/UpdateInstancePartitionRequest.php @@ -24,7 +24,7 @@ class UpdateInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.InstancePartition instance_partition = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance_partition = null; + protected $instance_partition = null; /** * Required. A mask specifying which fields in * [InstancePartition][google.spanner.admin.instance.v1.InstancePartition] @@ -35,7 +35,7 @@ class UpdateInstancePartitionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.FieldMask field_mask = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $field_mask = null; + protected $field_mask = null; /** * @param \Google\Cloud\Spanner\Admin\Instance\V1\InstancePartition $instancePartition Required. The instance partition to update, which must always include the diff --git a/Spanner/src/Admin/Instance/V1/UpdateInstanceRequest.php b/Spanner/src/Admin/Instance/V1/UpdateInstanceRequest.php index c45c97a6052d..f787f2e812ec 100644 --- a/Spanner/src/Admin/Instance/V1/UpdateInstanceRequest.php +++ b/Spanner/src/Admin/Instance/V1/UpdateInstanceRequest.php @@ -24,7 +24,7 @@ class UpdateInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.admin.instance.v1.Instance instance = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $instance = null; + protected $instance = null; /** * Required. A mask specifying which fields in * [Instance][google.spanner.admin.instance.v1.Instance] should be updated. @@ -34,7 +34,7 @@ class UpdateInstanceRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.FieldMask field_mask = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $field_mask = null; + protected $field_mask = null; /** * @param \Google\Cloud\Spanner\Admin\Instance\V1\Instance $instance Required. The instance to update, which must always include the instance diff --git a/Spanner/src/ArrayType.php b/Spanner/src/ArrayType.php index 518db7088370..db3aec8bcf4a 100644 --- a/Spanner/src/ArrayType.php +++ b/Spanner/src/ArrayType.php @@ -29,7 +29,7 @@ * use Google\Cloud\Spanner\Database; * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $database = $spanner->connect('my-instance', 'my-database'); * * $arrayType = new ArrayType(Database::TYPE_STRING); diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index 8f54a6297ba9..fe231bea5095 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -17,15 +17,20 @@ namespace Google\Cloud\Spanner; +use Closure; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; -use Google\Cloud\Core\ArrayTrait; +use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Core\Exception\NotFoundException; +use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Backup\State; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LROTrait; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; use DateTimeInterface; /** @@ -35,110 +40,41 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $backup = $spanner->instance('my-instance')->backup('my-backup'); * ``` - * - * @method resumeOperation() { - * Resume a long running operation - * - * Example: - * ``` - * $operation = $backup->resumeOperation($operationName); - * ``` - * - * @param string $operationName The long running operation name. - * @param array $info [optional] The operation data. - * @return LongRunningOperation - * } - * @method longRunningOperations() { - * List long running operations. - * - * Example: - * ``` - * $operations = $backup->longRunningOperations(); - * ``` - * - * @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 - * } */ class Backup { - use ArrayTrait; - use LROTrait; + use ApiHelperTrait; + use RequestProcessorTrait; + use RequestTrait; const STATE_READY = State::READY; const STATE_CREATING = State::CREATING; - /** - * @var ConnectionInterface - * @internal - */ - private $connection; - - /** - * @var Instance - */ - private $instance; - - /** - * @var string - */ - private $projectId; - - /** - * @var string - */ - private $name; - - /** - * @var array - */ - private $info; - /** * Create an object representing a Backup. * - * @param ConnectionInterface $connection The connection to the - * Cloud Spanner Admin API. This object is created by SpannerClient, - * and should not be instantiated outside of this client. + * @internal Backup is constructed by the {@see Instance} class. + * + * @param DatabaseAdminClient The database admin client to make backup RPC calls. + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param Instance $instance The instance in which the backup exists. - * @param LongRunningConnectionInterface $lroConnection An implementation - * mapping to methods which handle LRO resolution in the service. - * @param array $lroCallables * @param string $projectId The project ID. * @param string $name The backup name or ID. * @param array $info [optional] An array representing the backup resource. */ public function __construct( - ConnectionInterface $connection, - Instance $instance, - LongRunningConnectionInterface $lroConnection, - array $lroCallables, - $projectId, - $name, - array $info = [] + private DatabaseAdminClient $databaseAdminClient, + private Serializer $serializer, + private Instance $instance, + private $projectId, + private string $name, + private array $info = [] ) { - $this->connection = $connection; - $this->instance = $instance; - $this->projectId = $projectId; $this->name = $this->fullyQualifiedBackupName($name); - $this->info = $info; - - $this->setLroProperties($lroConnection, $lroCallables, $this->name); } /** @@ -161,32 +97,32 @@ public function __construct( * consistent copy of the database. If not present, it will be the same * as the create time of the backup. * } - * @return LongRunningOperation + * @return OperationResponse * @throws \InvalidArgumentException */ public function create($database, DateTimeInterface $expireTime, array $options = []) { - if (isset($options['versionTime'])) { - if (!($options['versionTime'] instanceof DateTimeInterface)) { - throw new \InvalidArgumentException( - 'Optional argument `versionTime` must be a DateTimeInterface, got ' . - (is_object($options['versionTime']) - ? get_class($options['versionTime']) - : gettype($options['versionTime'])) - ); - } - $options['versionTime'] = $options['versionTime']->format('Y-m-d\TH:i:s.u\Z'); - } - $operation = $this->connection->createBackup([ - 'instance' => $this->instance->name(), + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data = $this->validateAndFormatVersionTime($data); + + $data += [ + 'parent' => $this->instance->name(), 'backupId' => DatabaseAdminClient::parseName($this->name)['backup'], 'backup' => [ 'database' => $this->instance->database($database)->name(), - 'expireTime' => $expireTime->format('Y-m-d\TH:i:s.u\Z'), + 'expireTime' => $this->formatTimestampForApi($expireTime->format('Y-m-d\TH:i:s.u\Z')) ], - ] + $options); + ]; + if (isset($data['versionTime'])) { + $data['backup']['versionTime'] = $data['versionTime']; + unset($data['versionTime']); + } - return $this->resumeOperation($operation['name'], $operation); + $request = $this->serializer->decodeMessage(new CreateBackupRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); + + return $this->databaseAdminClient->createBackup($request, $callOptions) + ->withResultFunction($this->backupResultFunction()); } /** @@ -209,24 +145,25 @@ public function create($database, DateTimeInterface $expireTime, array $options * eligible to be automatically deleted by Cloud Spanner. * @param array $options [optional] { * Configuration Options. - * - * @type DateTimeInterface $versionTime The version time for the externally - * consistent copy of the database. If not present, it will be the same - * as the create time of the backup. * } - * @return LongRunningOperation + * @return OperationResponse * @throws \InvalidArgumentException */ public function createCopy(Backup $newBackup, DateTimeInterface $expireTime, array $options = []) { - $operation = $this->connection->copyBackup([ - 'instance' => $newBackup->instance->name(), + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'parent' => $newBackup->instance->name(), 'backupId' => DatabaseAdminClient::parseName($newBackup->name)['backup'], - 'sourceBackupId' => $this->fullyQualifiedBackupName($this->name), - 'expireTime' => $expireTime->format('Y-m-d\TH:i:s.u\Z') - ] + $options); + 'sourceBackup' => $this->fullyQualifiedBackupName($this->name), + 'expireTime' => $this->formatTimestampForApi($expireTime->format('Y-m-d\TH:i:s.u\Z')) + ]; + + $request = $this->serializer->decodeMessage(new CopyBackupRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - return $this->resumeOperation($operation['name'], $operation); + return $this->databaseAdminClient->copyBackup($request, $callOptions) + ->withResultFunction($this->backupResultFunction()); } /** @@ -242,7 +179,15 @@ public function createCopy(Backup $newBackup, DateTimeInterface $expireTime, arr */ public function delete(array $options = []) { - return $this->connection->deleteBackup(['name' => $this->name] + $options); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'name' => $this->name + ]; + + $request = $this->serializer->decodeMessage(new DeleteBackupRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $this->databaseAdminClient->deleteBackup($request, $callOptions); } /** @@ -318,9 +263,16 @@ public function name() */ public function reload(array $options = []) { - return $this->info = $this->connection->getBackup([ + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ 'name' => $this->name - ] + $options); + ]; + + $request = $this->serializer->decodeMessage(new GetBackupRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->getBackup($request, $callOptions); + return $this->info = $this->handleResponse($response); } /** @@ -368,15 +320,43 @@ public function state(array $options = []) */ public function updateExpireTime(DateTimeInterface $newTimestamp, array $options = []) { - return $this->info = $this->connection->updateBackup([ + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ 'backup' => [ 'name' => $this->name(), - 'expireTime' => $newTimestamp->format('Y-m-d\TH:i:s.u\Z'), + 'expireTime' => $this->formatTimestampForApi( + $newTimestamp->format('Y-m-d\TH:i:s.u\Z') + ), ], 'updateMask' => [ 'paths' => ['expire_time'] ] - ] + $options); + ]; + + $request = $this->serializer->decodeMessage(new UpdateBackupRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->updateBackup($request, $callOptions); + return $this->info = $this->handleResponse($response); + } + + /** + * Resume a Long Running Operation + * + * Example: + * ``` + * $operation = $spanner->resumeOperation($operationName); + * ``` + * + * @param string $operationName The Long Running Operation name. + * @return OperationResponse + */ + public function resumeOperation($operationName) + { + return (new OperationResponse( + $operationName, + $this->databaseAdminClient->getOperationsClient() + ))->withResultFunction($this->backupResultFunction()); } /** @@ -400,4 +380,35 @@ private function fullyQualifiedBackupName($name) } //@codeCoverageIgnoreEnd } + + /** + * @param array $options + * @return array + */ + private function validateAndFormatVersionTime(array $options) + { + if (isset($options['versionTime'])) { + if (!($options['versionTime'] instanceof DateTimeInterface)) { + throw new \InvalidArgumentException( + 'Optional argument `versionTime` must be a DateTimeInterface, got ' . + (is_object($options['versionTime']) + ? get_class($options['versionTime']) + : gettype($options['versionTime'])) + ); + } + $options['versionTime'] = $this->formatTimestampForApi( + $options['versionTime']->format('Y-m-d\TH:i:s.u\Z') + ); + } + return $options; + } + + private function backupResultFunction(): Closure + { + return function (BackupProto $backup) { + $name = DatabaseAdminClient::parseName($backup->getName()); + $info = $this->serializer->decodeMessage($backup); + return $this->instance->backup($name['name'], $info); + }; + } } diff --git a/Spanner/src/Batch/BatchClient.php b/Spanner/src/Batch/BatchClient.php index 1c72a60cf7aa..be35db4304fc 100644 --- a/Spanner/src/Batch/BatchClient.php +++ b/Spanner/src/Batch/BatchClient.php @@ -18,10 +18,10 @@ namespace Google\Cloud\Spanner\Batch; use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\TransactionConfigurationTrait; +use Google\Protobuf\Duration; /** * Provides Batch APIs used to read data from a Cloud Spanner database. @@ -36,7 +36,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $batch = $spanner->batch('instance-id', 'database-id'); * ``` * diff --git a/Spanner/src/Batch/BatchSnapshot.php b/Spanner/src/Batch/BatchSnapshot.php index 301ff4ae8f44..def2870f697d 100644 --- a/Spanner/src/Batch/BatchSnapshot.php +++ b/Spanner/src/Batch/BatchSnapshot.php @@ -17,6 +17,7 @@ namespace Google\Cloud\Spanner\Batch; +use Google\ApiCore\Serializer; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; @@ -41,7 +42,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $batch = $spanner->batch('instance-id', 'database-id'); * $snapshot = $batch->snapshot(); * ``` diff --git a/Spanner/src/Batch/QueryPartition.php b/Spanner/src/Batch/QueryPartition.php index a8fa0a3411ca..acdcc3a684f9 100644 --- a/Spanner/src/Batch/QueryPartition.php +++ b/Spanner/src/Batch/QueryPartition.php @@ -35,7 +35,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $batch = $spanner->batch('instance-id', 'database-id'); * $snapshot = $batch->snapshot(); * diff --git a/Spanner/src/Batch/ReadPartition.php b/Spanner/src/Batch/ReadPartition.php index 22375864bb11..563c519f1594 100644 --- a/Spanner/src/Batch/ReadPartition.php +++ b/Spanner/src/Batch/ReadPartition.php @@ -36,7 +36,7 @@ * use Google\Cloud\Spanner\KeySet; * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $batch = $spanner->batch('instance-id', 'database-id'); * $snapshot = $batch->snapshot(); * diff --git a/Spanner/src/BatchDmlResult.php b/Spanner/src/BatchDmlResult.php index f375d8ea6a0e..25fa169f5f92 100644 --- a/Spanner/src/BatchDmlResult.php +++ b/Spanner/src/BatchDmlResult.php @@ -25,7 +25,7 @@ * use Google\Cloud\Spanner\SpannerClient; * use Google\Cloud\Spanner\Transaction; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $database = $spanner->connect('my-instance', 'my-database'); * * $batchDmlResult = $database->runTransaction(function (Transaction $t) { diff --git a/Spanner/src/Bytes.php b/Spanner/src/Bytes.php index 0fc38020c079..7d7120031dcd 100644 --- a/Spanner/src/Bytes.php +++ b/Spanner/src/Bytes.php @@ -28,7 +28,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $bytes = $spanner->bytes('hello world'); * ``` diff --git a/Spanner/src/CommitTimestamp.php b/Spanner/src/CommitTimestamp.php index d5aa05e59fe4..6b6caf91cf0d 100644 --- a/Spanner/src/CommitTimestamp.php +++ b/Spanner/src/CommitTimestamp.php @@ -41,7 +41,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $database = $spanner->connect('my-instance', 'my-database'); * * $database->insert('myTable', [ diff --git a/Spanner/src/Connection/ConnectionInterface.php b/Spanner/src/Connection/ConnectionInterface.php deleted file mode 100644 index 24f417672720..000000000000 --- a/Spanner/src/Connection/ConnectionInterface.php +++ /dev/null @@ -1,291 +0,0 @@ - 'setInsert', - 'update' => 'setUpdate', - 'insertOrUpdate' => 'setInsertOrUpdate', - 'replace' => 'setReplace', - '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 - */ - private $credentialsWrapper; - - /** - * @var bool - */ - private $larEnabled; - - /** - * @param array $config [optional] - */ - public function __construct(array $config = []) - { - //@codeCoverageIgnoreStart - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ], [], [], [ - // A custom encoder that short-circuits the encodeMessage in Serializer class, - // but only if the argument is of the type PartialResultSet. - PartialResultSet::class => function ($msg) { - $data = json_decode($msg->serializeToJsonString(), true); - - // We only override metadata fields, if it actually exists in the response. - // This is specially important for large data sets which is received in chunks. - // Metadata is only received in the first 'chunk' and we don't want to set empty metadata fields - // when metadata was not returned from the server. - if (isset($data['metadata'])) { - // The transaction id is serialized as a base64 encoded string in $data. So, we - // add a step to get the transaction id using a getter instead of the serialized value. - // The null-safe operator is used to handle edge cases where the relevant fields are not present. - $data['metadata']['transaction']['id'] = (string) $msg?->getMetadata()?->getTransaction()?->getId(); - - // Helps convert metadata enum values from string types to their respective code/annotation - // pairs. Ex: INT64 is converted to {code: 2, typeAnnotation: 0}. - $fields = $msg->getMetadata()?->getRowType()?->getFields(); - $data['metadata']['rowType']['fields'] = $this->getFieldDataFromRepeatedFields($fields); - } - - // These fields in stats should be an int - if (isset($data['stats']['rowCountLowerBound'])) { - $data['stats']['rowCountLowerBound'] = (int) $data['stats']['rowCountLowerBound']; - } - if (isset($data['stats']['rowCountExact'])) { - $data['stats']['rowCountExact'] = (int) $data['stats']['rowCountExact']; - } - - return $data; - } - ]); - //@codeCoverageIgnoreEnd - - $config['serializer'] = $this->serializer; - $this->setRequestWrapper(new GrpcRequestWrapper($config)); - $grpcConfig = $this->getGaxConfig( - ManualSpannerClient::VERSION, - isset($config['authHttpHandler']) - ? $config['authHttpHandler'] - : null - ); - - $config += [ - 'emulatorHost' => null, - 'queryOptions' => [] - ]; - if ((bool) $config['emulatorHost']) { - $grpcConfig = array_merge( - $grpcConfig, - $this->emulatorGapicConfig($config['emulatorHost']) - ); - } elseif (isset($config['apiEndpoint'])) { - $grpcConfig['apiEndpoint'] = $config['apiEndpoint']; - } - $this->credentialsWrapper = $grpcConfig['credentials']; - - $this->defaultQueryOptions = $config['queryOptions']; - - $this->spannerClient = $config['gapicSpannerClient'] - ?? $this->constructGapic(SpannerClient::class, $grpcConfig); - - //@codeCoverageIgnoreStart - if (isset($config['gapicSpannerInstanceAdminClient'])) { - $this->instanceAdminClient = $config['gapicSpannerInstanceAdminClient']; - } - - if (isset($config['gapicSpannerDatabaseAdminClient'])) { - $this->databaseAdminClient = $config['gapicSpannerDatabaseAdminClient']; - } - //@codeCoverageIgnoreEnd - - $this->grpcConfig = $grpcConfig; - $this->larEnabled = $this->pluck('routeToLeader', $config, false) ?? true; - } - - /** - * @param array $args - */ - public function listInstanceConfigs(array $args) - { - $projectName = $this->pluck('projectName', $args); - return $this->send([$this->getInstanceAdminClient(), 'listInstanceConfigs'], [ - $projectName, - $this->addResourcePrefixHeader($args, $projectName) - ]); - } - - /** - * @param array $args - */ - public function getInstanceConfig(array $args) - { - $projectName = $this->pluck('projectName', $args); - return $this->send([$this->getInstanceAdminClient(), 'getInstanceConfig'], [ - $this->pluck('name', $args), - $this->addResourcePrefixHeader($args, $projectName) - ]); - } - - /** - * @param array $args - */ - public function createInstanceConfig(array $args) - { - $instanceConfigName = $args['name']; - - $instanceConfig = $this->instanceConfigObject($args, true); - $res = $this->send([$this->getInstanceAdminClient(), 'createInstanceConfig'], [ - $this->pluck('projectName', $args), - $this->pluck('instanceConfigId', $args), - $instanceConfig, - $this->addResourcePrefixHeader($args, $instanceConfigName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function updateInstanceConfig(array $args) - { - $instanceConfigName = $args['name']; - - $instanceConfigArray = $this->instanceConfigArray($args); - - $fieldMask = $this->fieldMask($instanceConfigArray); - - $instanceConfigObject = $this->serializer->decodeMessage(new InstanceConfig(), $instanceConfigArray); - - $res = $this->send([$this->getInstanceAdminClient(), 'updateInstanceConfig'], [ - $instanceConfigObject, - $fieldMask, - $this->addResourcePrefixHeader($args, $instanceConfigName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function deleteInstanceConfig(array $args) - { - $instanceConfigName = $this->pluck('name', $args); - return $this->send([$this->getInstanceAdminClient(), 'deleteInstanceConfig'], [ - $instanceConfigName, - $this->addResourcePrefixHeader($args, $instanceConfigName) - ]); - } - - /** - * @param array $args - */ - public function listInstanceConfigOperations(array $args) - { - $projectName = $this->pluck('projectName', $args); - $result = $this->send([$this->getInstanceAdminClient(), 'listInstanceConfigOperations'], [ - $projectName, - $this->addResourcePrefixHeader($args, $projectName) - ]); - foreach ($result['operations'] as $index => $operation) { - $result['operations'][$index] = $this->deserializeOperationArray($operation); - } - return $result; - } - - /** - * @param array $args - */ - public function listInstances(array $args) - { - $projectName = $this->pluck('projectName', $args); - return $this->send([$this->getInstanceAdminClient(), 'listInstances'], [ - $projectName, - $this->addResourcePrefixHeader($args, $projectName) - ]); - } - - /** - * @param array $args - */ - public function getInstance(array $args) - { - $projectName = $this->pluck('projectName', $args); - - if (isset($args['fieldMask'])) { - $mask = []; - if (is_array($args['fieldMask'])) { - foreach (array_values($args['fieldMask']) as $field) { - $mask[] = Serializer::toSnakeCase($field); - } - } else { - $mask[] = Serializer::toSnakeCase($args['fieldMask']); - } - $fieldMask = $this->serializer->decodeMessage(new FieldMask(), ['paths' => $mask]); - $args['fieldMask'] = $fieldMask; - } - - return $this->send([$this->getInstanceAdminClient(), 'getInstance'], [ - $this->pluck('name', $args), - $this->addResourcePrefixHeader($args, $projectName) - ]); - } - - /** - * @param array $args - */ - public function createInstance(array $args) - { - $instanceName = $args['name']; - - $instance = $this->instanceObject($args, true); - $res = $this->send([$this->getInstanceAdminClient(), 'createInstance'], [ - $this->pluck('projectName', $args), - $this->pluck('instanceId', $args), - $instance, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function updateInstance(array $args) - { - $instanceName = $args['name']; - - $instanceArray = $this->instanceArray($args); - - $fieldMask = $this->fieldMask($instanceArray); - - $instanceObject = $this->serializer->decodeMessage(new Instance(), $instanceArray); - - $res = $this->send([$this->getInstanceAdminClient(), 'updateInstance'], [ - $instanceObject, - $fieldMask, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function deleteInstance(array $args) - { - $instanceName = $this->pluck('name', $args); - return $this->send([$this->getInstanceAdminClient(), 'deleteInstance'], [ - $instanceName, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - } - - /** - * @param array $args - */ - public function getInstanceIamPolicy(array $args) - { - $resource = $this->pluck('resource', $args); - return $this->send([$this->getInstanceAdminClient(), 'getIamPolicy'], [ - $resource, - $this->addResourcePrefixHeader($args, $resource) - ]); - } - - /** - * @param array $args - */ - public function setInstanceIamPolicy(array $args) - { - $resource = $this->pluck('resource', $args); - return $this->send([$this->getInstanceAdminClient(), 'setIamPolicy'], [ - $resource, - $this->pluck('policy', $args), - $this->addResourcePrefixHeader($args, $resource) - ]); - } - - /** - * @param array $args - */ - public function testInstanceIamPermissions(array $args) - { - $resource = $this->pluck('resource', $args); - return $this->send([$this->getInstanceAdminClient(), 'testIamPermissions'], [ - $resource, - $this->pluck('permissions', $args), - $this->addResourcePrefixHeader($args, $resource) - ]); - } - - /** - * @param array $args - */ - public function listBackups(array $args) - { - $instanceName = $this->pluck('instance', $args); - return $this->send([$this->getDatabaseAdminClient(), 'listBackups'], [ - $instanceName, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - } - - /** - * @param array $args - */ - public function listBackupOperations(array $args) - { - $instanceName = $this->pluck('instance', $args); - $result = $this->send([$this->getDatabaseAdminClient(), 'listBackupOperations'], [ - $instanceName, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - foreach ($result['operations'] as $index => $operation) { - $result['operations'][$index] = $this->deserializeOperationArray($operation); - } - return $result; - } - - /** - * @param array $args - */ - public function listDatabaseOperations(array $args) - { - $instanceName = $this->pluck('instance', $args); - $result = $this->send([$this->getDatabaseAdminClient(), 'listDatabaseOperations'], [ - $instanceName, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - foreach ($result['operations'] as $index => $operation) { - $result['operations'][$index] = $this->deserializeOperationArray($operation); - } - return $result; - } - - /** - * @param array $args - */ - public function restoreDatabase(array $args) - { - $instanceName = $this->pluck('instance', $args); - if (isset($args['encryptionConfig'])) { - $args['encryptionConfig'] = $this->serializer->decodeMessage( - new RestoreDatabaseEncryptionConfig, - $this->pluck('encryptionConfig', $args) - ); - } - $res = $this->send([$this->getDatabaseAdminClient(), 'restoreDatabase'], [ - $instanceName, - $this->pluck('databaseId', $args), - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function updateBackup(array $args) - { - $backup = $this->pluck('backup', $args); - $backup['expireTime'] = $this->formatTimestampForApi($this->pluck('expireTime', $backup)); - $backupInfo = $this->serializer->decodeMessage(new Backup(), $backup); - - $backupName = $backupInfo->getName(); - $updateMask = $this->serializer->decodeMessage(new FieldMask(), $this->pluck('updateMask', $args)); - return $this->send([$this->getDatabaseAdminClient(), 'updateBackup'], [ - $backupInfo, - $updateMask, - $this->addResourcePrefixHeader($args, $backupName) - ]); - } - - /** - * @param array $args - */ - public function createBackup(array $args) - { - $backup = $this->pluck('backup', $args); - $backup['expireTime'] = $this->formatTimestampForApi($this->pluck('expireTime', $backup)); - if (isset($args['versionTime'])) { - $backup['versionTime'] = $this->formatTimestampForApi($this->pluck('versionTime', $args)); - } - $backupInfo = $this->serializer->decodeMessage(new Backup(), $backup); - - $instanceName = $this->pluck('instance', $args); - if (isset($args['encryptionConfig'])) { - $args['encryptionConfig'] = $this->serializer->decodeMessage( - new CreateBackupEncryptionConfig, - $this->pluck('encryptionConfig', $args) - ); - } - $res = $this->send([$this->getDatabaseAdminClient(), 'createBackup'], [ - $instanceName, - $this->pluck('backupId', $args), - $backupInfo, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function copyBackup(array $args) - { - $instanceName = $this->pluck('instance', $args); - $expireTime = new Timestamp( - $this->formatTimestampForApi($this->pluck('expireTime', $args)) - ); - - $res = $this->send([$this->getDatabaseAdminClient(), 'copyBackup'], [ - $instanceName, - $this->pluck('backupId', $args), - $this->pluck('sourceBackupId', $args), - $expireTime, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function deleteBackup(array $args) - { - $backupName = $this->pluck('name', $args); - return $this->send([$this->getDatabaseAdminClient(), 'deleteBackup'], [ - $backupName, - $this->addResourcePrefixHeader($args, $backupName) - ]); - } - - /** - * @param array $args - */ - public function getBackup(array $args) - { - $backupName = $this->pluck('name', $args); - return $this->send([$this->getDatabaseAdminClient(), 'getBackup'], [ - $backupName, - $this->addResourcePrefixHeader($args, $backupName) - ]); - } - - /** - * @param array $args - */ - public function listDatabases(array $args) - { - $instanceName = $this->pluck('instance', $args); - return $this->send([$this->getDatabaseAdminClient(), 'listDatabases'], [ - $instanceName, - $this->addResourcePrefixHeader($args, $instanceName) - ]); - } - - /** - * @param array $args - */ - public function createDatabase(array $args) - { - $instanceName = $this->pluck('instance', $args); - if (isset($args['encryptionConfig'])) { - $args['encryptionConfig'] = $this->serializer->decodeMessage( - new EncryptionConfig, - $this->pluck('encryptionConfig', $args) - ); - } - $res = $this->send([$this->getDatabaseAdminClient(), 'createDatabase'], [ - $instanceName, - $this->pluck('createStatement', $args), - $this->addResourcePrefixHeader($args, $instanceName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function updateDatabase(array $args) - { - $databaseInfo = $this->serializer->decodeMessage(new Database(), $this->pluck('database', $args)); - $databaseName = $databaseInfo->getName(); - $updateMask = $this->serializer->decodeMessage(new FieldMask(), $this->pluck('updateMask', $args)); - return $this->send([$this->getDatabaseAdminClient(), 'updateDatabase'], [ - $databaseInfo, - $updateMask, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function updateDatabaseDdl(array $args) - { - $databaseName = $this->pluck('name', $args); - $res = $this->send([$this->getDatabaseAdminClient(), 'updateDatabaseDdl'], [ - $databaseName, - $this->pluck('statements', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - - return $this->operationToArray($res, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function dropDatabase(array $args) - { - $databaseName = $this->pluck('name', $args); - return $this->send([$this->getDatabaseAdminClient(), 'dropDatabase'], [ - $databaseName, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function getDatabase(array $args) - { - $databaseName = $this->pluck('name', $args); - return $this->send([$this->getDatabaseAdminClient(), 'getDatabase'], [ - $databaseName, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function getDatabaseDdl(array $args) - { - $databaseName = $this->pluck('name', $args); - return $this->send([$this->getDatabaseAdminClient(), 'getDatabaseDdl'], [ - $databaseName, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function getDatabaseIamPolicy(array $args) - { - $databaseName = $this->pluck('resource', $args); - return $this->send([$this->getDatabaseAdminClient(), 'getIamPolicy'], [ - $databaseName, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function setDatabaseIamPolicy(array $args) - { - $databaseName = $this->pluck('resource', $args); - return $this->send([$this->getDatabaseAdminClient(), 'setIamPolicy'], [ - $databaseName, - $this->pluck('policy', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function testDatabaseIamPermissions(array $args) - { - $databaseName = $this->pluck('resource', $args); - return $this->send([$this->getDatabaseAdminClient(), 'testIamPermissions'], [ - $databaseName, - $this->pluck('permissions', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function createSession(array $args) - { - $databaseName = $this->pluck('database', $args); - - $session = $this->pluck('session', $args, false); - if ($session) { - $args['session'] = $this->serializer->decodeMessage( - new Session, - array_filter( - $session, - function ($value) { - return !is_null($value); - } - ) - ); - } - - $args = $this->addLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'createSession'], [ - $databaseName, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * Note: This should be removed once GAPIC exposes the ability to execute - * concurrent requests. - * - * @access private - * @experimental - * @param array $args - * @return PromiseInterface - */ - public function createSessionAsync(array $args) - { - $databaseName = $this->pluck('database', $args); - $opts = $this->addResourcePrefixHeader([], $databaseName); - $opts = $this->addLarHeader($opts, $this->larEnabled); - $opts['credentialsWrapper'] = $this->credentialsWrapper; - $transport = $this->spannerClient->getTransport(); - - $request = new CreateSessionRequest([ - 'database' => $databaseName - ]); - - $session = $this->pluck('session', $args, false); - - if ($session) { - $sessionMessage = new Session($session); - $request->setSession($sessionMessage); - } - - return $transport->startUnaryCall( - new Call( - 'google.spanner.v1.Spanner/CreateSession', - Session::class, - $request - ), - $opts - ); - } - - /** - * @param array $args - */ - public function batchCreateSessions(array $args) - { - $args['sessionTemplate'] = $this->serializer->decodeMessage( - new Session, - $this->pluck('sessionTemplate', $args) - ); - - $databaseName = $this->pluck('database', $args); - $args = $this->addLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'batchCreateSessions'], [ - $databaseName, - $this->pluck('sessionCount', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function getSession(array $args) - { - $databaseName = $this->pluck('database', $args); - $args = $this->addLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'getSession'], [ - $this->pluck('name', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function deleteSession(array $args) - { - $databaseName = $this->pluck('database', $args); - return $this->send([$this->spannerClient, 'deleteSession'], [ - $this->pluck('name', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * Note: This should be removed once GAPIC exposes the ability to execute - * concurrent requests. - * - * @access private - * @param array $args - * @return PromiseInterface - * @experimental - */ - public function deleteSessionAsync(array $args) - { - $databaseName = $this->pluck('database', $args); - $request = new DeleteSessionRequest(); - $request->setName($this->pluck('name', $args)); - - $transport = $this->spannerClient->getTransport(); - $opts = $this->addResourcePrefixHeader([], $databaseName); - $opts['credentialsWrapper'] = $this->credentialsWrapper; - - return $transport->startUnaryCall( - new Call( - 'google.spanner.v1.Spanner/DeleteSession', - GPBEmpty::class, - $request - ), - $opts - ); - } - - /** - * @param array $args - * @return \Generator - */ - public function executeStreamingSql(array $args) - { - $args = $this->formatSqlParams($args); - $args['transaction'] = $this->createTransactionSelector($args); - - $databaseName = $this->pluck('database', $args); - $queryOptions = $this->pluck('queryOptions', $args, false) ?: []; - - // Query options precedence is query-level, then environment-level, then client-level. - $envQueryOptimizerVersion = getenv('SPANNER_OPTIMIZER_VERSION'); - $envQueryOptimizerStatisticsPackage = getenv('SPANNER_OPTIMIZER_STATISTICS_PACKAGE'); - if (!empty($envQueryOptimizerVersion)) { - $queryOptions += ['optimizerVersion' => $envQueryOptimizerVersion]; - } - if (!empty($envQueryOptimizerStatisticsPackage)) { - $queryOptions += ['optimizerStatisticsPackage' => $envQueryOptimizerStatisticsPackage]; - } - $queryOptions += $this->defaultQueryOptions; - $this->setDirectedReadOptions($args); - - if ($queryOptions) { - $args['queryOptions'] = $this->serializer->decodeMessage( - new QueryOptions, - $queryOptions - ); - } - - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - - $args = $this->conditionallyUnsetLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'executeStreamingSql'], [ - $this->pluck('session', $args), - $this->pluck('sql', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - * @return \Generator - */ - public function streamingRead(array $args) - { - $keySet = $this->pluck('keySet', $args); - $keySet = $this->serializer->decodeMessage(new KeySet, $this->formatKeySet($keySet)); - - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - $this->setDirectedReadOptions($args); - $args['transaction'] = $this->createTransactionSelector($args); - - $databaseName = $this->pluck('database', $args); - $args = $this->conditionallyUnsetLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'streamingRead'], [ - $this->pluck('session', $args), - $this->pluck('table', $args), - $this->pluck('columns', $args), - $keySet, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function executeBatchDml(array $args) - { - $databaseName = $this->pluck('database', $args); - $args['transaction'] = $this->createTransactionSelector($args); - $args = $this->addLarHeader($args, $this->larEnabled); - - $statements = []; - foreach ($this->pluck('statements', $args) as $statement) { - $statement = $this->formatSqlParams($statement); - $statements[] = $this->serializer->decodeMessage(new Statement, $statement); - } - - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - - return $this->send([$this->spannerClient, 'executeBatchDml'], [ - $this->pluck('session', $args), - $this->pluck('transaction', $args), - $statements, - $this->pluck('seqno', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function beginTransaction(array $args) - { - $options = new TransactionOptions; - $transactionOptions = $this->formatTransactionOptions($this->pluck('transactionOptions', $args)); - if (isset($transactionOptions['readOnly'])) { - $readOnlyClass = PHP_VERSION_ID >= 80100 - ? PBReadOnly::class - : 'Google\Cloud\Spanner\V1\TransactionOptions\ReadOnly'; - $readOnly = $this->serializer->decodeMessage( - new $readOnlyClass(), // @phpstan-ignore-line - $transactionOptions['readOnly'] - ); - $options->setReadOnly($readOnly); - } elseif (isset($transactionOptions['readWrite'])) { - $readWrite = new ReadWrite(); - $options->setReadWrite($readWrite); - $args = $this->addLarHeader($args, $this->larEnabled); - } elseif (isset($transactionOptions['partitionedDml'])) { - $pdml = new PartitionedDml(); - $options->setPartitionedDml($pdml); - $args = $this->addLarHeader($args, $this->larEnabled); - } - - // NOTE: if set for read-only actions, will throw exception - if (isset($transactionOptions['excludeTxnFromChangeStreams'])) { - $options->setExcludeTxnFromChangeStreams( - $transactionOptions['excludeTxnFromChangeStreams'] - ); - } - - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - - $databaseName = $this->pluck('database', $args); - return $this->send([$this->spannerClient, 'beginTransaction'], [ - $this->pluck('session', $args), - $options, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function commit(array $args) - { - $inputMutations = $this->pluck('mutations', $args); - - $mutations = $this->parseMutations($inputMutations); - - if (isset($args['singleUseTransaction'])) { - $readWrite = $this->serializer->decodeMessage( - new ReadWrite, - [] - ); - - $options = new TransactionOptions; - $options->setReadWrite($readWrite); - $args['singleUseTransaction'] = $options; - } - - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - - $databaseName = $this->pluck('database', $args); - $args = $this->addLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'commit'], [ - $this->pluck('session', $args), - $mutations, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - * @return \Generator - */ - public function batchWrite(array $args) - { - $databaseName = $this->pluck('database', $args); - $mutationGroups = $this->pluck('mutationGroups', $args); - $requestOptions = $this->pluck('requestOptions', $args, false) ?: []; - - array_walk( - $mutationGroups, - fn(&$x) => $x['mutations'] = $this->parseMutations($x['mutations']) - ); - - $mutationGroups = array_map( - fn($x) => $this->serializer->decodeMessage(new MutationGroupProto(), $x), - $mutationGroups - ); - - if ($requestOptions) { - $args['requestOptions'] = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - } - - return $this->send([$this->spannerClient, 'batchWrite'], [ - $this->pluck('session', $args), - $mutationGroups, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function rollback(array $args) - { - $databaseName = $this->pluck('database', $args); - $args = $this->addLarHeader($args, $this->larEnabled); - return $this->send([$this->spannerClient, 'rollback'], [ - $this->pluck('session', $args), - $this->pluck('transactionId', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function partitionQuery(array $args) - { - $args = $this->formatSqlParams($args); - $args['transaction'] = $this->createTransactionSelector($args); - $args = $this->addLarHeader($args, $this->larEnabled); - - $args['partitionOptions'] = $this->serializer->decodeMessage( - new PartitionOptions, - $this->pluck('partitionOptions', $args, false) ?: [] - ); - - $databaseName = $this->pluck('database', $args); - return $this->send([$this->spannerClient, 'partitionQuery'], [ - $this->pluck('session', $args), - $this->pluck('sql', $args), - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function partitionRead(array $args) - { - $keySet = $this->pluck('keySet', $args); - $keySet = $this->serializer->decodeMessage(new KeySet, $this->formatKeySet($keySet)); - - $args['transaction'] = $this->createTransactionSelector($args); - $args = $this->addLarHeader($args, $this->larEnabled); - - $args['partitionOptions'] = $this->serializer->decodeMessage( - new PartitionOptions, - $this->pluck('partitionOptions', $args, false) ?: [] - ); - - $databaseName = $this->pluck('database', $args); - return $this->send([$this->spannerClient, 'partitionRead'], [ - $this->pluck('session', $args), - $this->pluck('table', $args), - $keySet, - $this->addResourcePrefixHeader($args, $databaseName) - ]); - } - - /** - * @param array $args - */ - public function getOperation(array $args) - { - $name = $this->pluck('name', $args); - - $operation = $this->getOperationByName($this->getDatabaseAdminClient(), $name); - - return $this->operationToArray($operation, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function cancelOperation(array $args) - { - $name = $this->pluck('name', $args); - $method = $this->pluck('method', $args, false); - - $operation = $this->getOperationByName($this->getDatabaseAdminClient(), $name, $method); - $operation->cancel(); - - return $this->operationToArray($operation, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function deleteOperation(array $args) - { - $name = $this->pluck('name', $args); - $method = $this->pluck('method', $args, false); - - $operation = $this->getOperationByName($this->getDatabaseAdminClient(), $name, $method); - $operation->delete(); - - return $this->operationToArray($operation, $this->serializer, $this->lroResponseMappers); - } - - /** - * @param array $args - */ - public function listOperations(array $args) - { - $name = $this->pluck('name', $args, false) ?: ''; - $filter = $this->pluck('filter', $args, false) ?: ''; - - $client = $this->getDatabaseAdminClient()->getOperationsClient(); - - return $this->send([$client, 'listOperations'], [ - $name, - $filter, - $args - ]); - } - - /** - * @param array $args - * @return array - */ - private function formatSqlParams(array $args) - { - $params = $this->pluck('params', $args); - if ($params) { - $modifiedParams = []; - foreach ($params as $key => $param) { - $modifiedParams[$key] = $this->fieldValue($param); - } - $args['params'] = new Struct; - $args['params']->setFields($modifiedParams); - } - - if (isset($args['paramTypes']) && is_array($args['paramTypes'])) { - foreach ($args['paramTypes'] as $key => $param) { - $args['paramTypes'][$key] = $this->serializer->decodeMessage(new Type, $param); - } - } - - return $args; - } - - /** - * @param array $keySet - * @return array Formatted keyset - */ - private function formatKeySet(array $keySet) - { - $keys = $this->pluck('keys', $keySet, false); - if ($keys) { - $keySet['keys'] = []; - - foreach ($keys as $key) { - $keySet['keys'][] = $this->formatListForApi((array) $key); - } - } - - if (isset($keySet['ranges'])) { - foreach ($keySet['ranges'] as $index => $rangeItem) { - foreach ($rangeItem as $key => $val) { - $rangeItem[$key] = $this->formatListForApi($val); - } - - $keySet['ranges'][$index] = $rangeItem; - } - - if (empty($keySet['ranges'])) { - unset($keySet['ranges']); - } - } - - return $keySet; - } - - /** - * @param array $args - * @return TransactionSelector - */ - private function createTransactionSelector(array &$args) - { - $selector = new TransactionSelector; - if (isset($args['transaction'])) { - $transaction = $this->pluck('transaction', $args); - - if (isset($transaction['singleUse'])) { - $transaction['singleUse'] = $this->formatTransactionOptions($transaction['singleUse']); - } - - if (isset($transaction['begin'])) { - $transaction['begin'] = $this->formatTransactionOptions($transaction['begin']); - } - - $selector = $this->serializer->decodeMessage($selector, $transaction); - } elseif (isset($args['transactionId'])) { - $selector = $this->serializer->decodeMessage($selector, ['id' => $this->pluck('transactionId', $args)]); - } - - return $selector; - } - - /** - * Converts a PHP array to an InstanceConfig proto message. - * - * @param array $args - * @param bool $required - * @return InstanceConfig - */ - private function instanceConfigObject(array &$args, $required = false) - { - return $this->serializer->decodeMessage( - new InstanceConfig(), - $this->instanceConfigArray($args, $required) - ); - } - - /** - * Creates a PHP array with only the fields that are relevant for an InstanceConfig. - * - * @param array $args - * @param bool $required - * @return array - */ - private function instanceConfigArray(array &$args, $required = false) - { - $argsCopy = $args; - return array_intersect_key([ - 'name' => $this->pluck('name', $args, $required), - 'baseConfig' => $this->pluck('baseConfig', $args, $required), - 'displayName' => $this->pluck('displayName', $args, $required), - 'configType' => $this->pluck('configType', $args, $required), - 'replicas' => $this->pluck('replicas', $args, $required), - 'optionalReplicas' => $this->pluck('optionalReplicas', $args, $required), - 'leaderOptions' => $this->pluck('leaderOptions', $args, $required), - 'reconciling' => $this->pluck('reconciling', $args, $required), - 'state' => $this->pluck('state', $args, $required), - 'labels' => $this->pluck('labels', $args, $required), - ], $argsCopy); - } - - /** - * @param array $args - * @param bool $required - * @return Instance - */ - private function instanceObject(array &$args, $required = false) - { - return $this->serializer->decodeMessage( - new Instance(), - $this->instanceArray($args, $required) - ); - } - - /** - * @param array $args - * @param bool $required - * @return array - */ - private function instanceArray(array &$args, $required = false) - { - $argsCopy = $args; - if (isset($args['nodeCount'])) { - return array_intersect_key([ - 'name' => $this->pluck('name', $args, $required), - 'config' => $this->pluck('config', $args, $required), - 'displayName' => $this->pluck('displayName', $args, $required), - 'nodeCount' => $this->pluck('nodeCount', $args, $required), - 'state' => $this->pluck('state', $args, $required), - 'labels' => $this->pluck('labels', $args, $required), - ], $argsCopy); - } - return array_intersect_key([ - 'name' => $this->pluck('name', $args, $required), - 'config' => $this->pluck('config', $args, $required), - 'displayName' => $this->pluck('displayName', $args, $required), - 'processingUnits' => $this->pluck('processingUnits', $args, $required), - 'state' => $this->pluck('state', $args, $required), - 'labels' => $this->pluck('labels', $args, $required), - ], $argsCopy); - } - - /** - * @param array $instanceArray - * @return FieldMask - */ - private function fieldMask($instanceArray) - { - $mask = []; - foreach (array_keys($instanceArray) as $key) { - if ($key !== 'name') { - $mask[] = Serializer::toSnakeCase($key); - } - } - return $this->serializer->decodeMessage(new FieldMask(), ['paths' => $mask]); - } - - /** - * @param mixed $param - * @return Value - */ - private function fieldValue($param) - { - $field = new Value; - $value = $this->formatValueForApi($param); - - $setter = null; - switch (array_keys($value)[0]) { - case 'string_value': - $setter = 'setStringValue'; - break; - case 'number_value': - $setter = 'setNumberValue'; - break; - case 'bool_value': - $setter = 'setBoolValue'; - break; - case 'null_value': - $setter = 'setNullValue'; - break; - case 'struct_value': - $setter = 'setStructValue'; - $modifiedParams = []; - foreach ($param as $key => $value) { - $modifiedParams[$key] = $this->fieldValue($value); - } - $value = new Struct; - $value->setFields($modifiedParams); - - break; - case 'list_value': - $setter = 'setListValue'; - $modifiedParams = []; - foreach ($param as $item) { - $modifiedParams[] = $this->fieldValue($item); - } - $list = new ListValue; - $list->setValues($modifiedParams); - $value = $list; - - break; - } - - $value = is_array($value) ? current($value) : $value; - if ($setter) { - $field->$setter($value); - } - - return $field; - } - - /** - * @param array $transactionOptions - * @return array - */ - private function formatTransactionOptions(array $transactionOptions) - { - if (isset($transactionOptions['readOnly'])) { - $ro = $transactionOptions['readOnly']; - if (isset($ro['minReadTimestamp'])) { - $ro['minReadTimestamp'] = $this->formatTimestampForApi($ro['minReadTimestamp']); - } - - if (isset($ro['readTimestamp'])) { - $ro['readTimestamp'] = $this->formatTimestampForApi($ro['readTimestamp']); - } - - $transactionOptions['readOnly'] = $ro; - } - - return $transactionOptions; - } - - /** - * Allow lazy instantiation of the instance admin client. - * - * @return InstanceAdminClient - */ - private function getInstanceAdminClient() - { - //@codeCoverageIgnoreStart - if ($this->instanceAdminClient) { - return $this->instanceAdminClient; - } - //@codeCoverageIgnoreEnd - $this->instanceAdminClient = $this->constructGapic(InstanceAdminClient::class, $this->grpcConfig); - - return $this->instanceAdminClient; - } - - /** - * Allow lazy instantiation of the database admin client. - * - * @return DatabaseAdminClient - */ - private function getDatabaseAdminClient() - { - //@codeCoverageIgnoreStart - if ($this->databaseAdminClient) { - return $this->databaseAdminClient; - } - //@codeCoverageIgnoreEnd - - $this->databaseAdminClient = $this->constructGapic(DatabaseAdminClient::class, $this->grpcConfig); - - return $this->databaseAdminClient; - } - - private function deserializeOperationArray($operation) - { - $operation['metadata'] = - $this->deserializeMessageArray($operation['metadata']) + - ['typeUrl' => $operation['metadata']['typeUrl']]; - - if (isset($operation['response']) and isset($operation['response']['typeUrl'])) { - $operation['response'] = $this->deserializeMessageArray($operation['response']); - } - - return $operation; - } - - private function deserializeMessageArray($message) - { - $typeUrl = $message['typeUrl']; - $mapper = $this->getLroResponseMapper($typeUrl); - if (!isset($mapper)) { - return $message; - } - - $className = $mapper['message']; - $response = new $className; - $response->mergeFromString($message['value']); - return $this->serializer->encodeMessage($response); - } - - private function getLroResponseMapper($typeUrl) - { - foreach ($this->lroResponseMappers as $mapper) { - if ($mapper['typeUrl'] == $typeUrl) { - return $mapper; - } - } - - return null; - } - - /** - * Set DirectedReadOptions if provided. - * - * @param array $args - */ - private function setDirectedReadOptions(array &$args) - { - $directedReadOptions = $this->pluck('directedReadOptions', $args, false); - if (!empty($directedReadOptions)) { - $args['directedReadOptions'] = $this->serializer->decodeMessage( - new DirectedReadOptions, - $directedReadOptions - ); - } - } - - /** - * Utiltiy method to take in a Google\Cloud\Spanner\V1\Type value and return - * the data as an array. The method takes care of array and struct elements. - * - * @param Type $type The "type" object - * - * @return array The formatted data. - */ - private function getTypeData(Type $type): array - { - $data = [ - 'code' => $type->getCode(), - 'typeAnnotation' => $type->getTypeAnnotation(), - 'protoTypeFqn' => $type->getProtoTypeFqn() - ]; - - // If this is a struct field, then recursisevly call getTypeData - if ($type->hasStructType()) { - $nestedType = $type->getStructType(); - $fields = $nestedType->getFields(); - $data['structType'] = [ - 'fields' => $this->getFieldDataFromRepeatedFields($fields) - ]; - } - // If this is an array field, then recursisevly call getTypeData - if ($type->hasArrayElementType()) { - $nestedType = $type->getArrayElementType(); - $data['arrayElementType'] = $this->getTypeData($nestedType); - } - - return $data; - } - - /** - * Utility method to return "fields data" in the format: - * [ - * "name" => "" - * "type" => [] - * ]. - * - * The type is converted from a string like INT64 to ["code" => 2, "typeAnnotation" => 0] - * conforming with the Google\Cloud\Spanner\V1\TypeCode class. - * - * @param ?RepeatedField $fields The array contain list of fields. - * - * @return array The formatted fields data. - */ - private function getFieldDataFromRepeatedFields(?RepeatedField $fields): array - { - if (is_null($fields)) { - return []; - } - - $fieldsData = []; - foreach ($fields as $key => $field) { - $type = $field->getType(); - $typeData = $this->getTypeData($type); - - $fieldsData[$key] = [ - 'name' => $field->getName(), - 'type' => $typeData - ]; - } - - return $fieldsData; - } - - private function parseMutations($rawMutations) - { - if (!is_array($rawMutations)) { - return []; - } - - $mutations = []; - foreach ($rawMutations as $mutation) { - $type = array_keys($mutation)[0]; - $data = $mutation[$type]; - - switch ($type) { - case Operation::OP_DELETE: - if (isset($data['keySet'])) { - $data['keySet'] = $this->formatKeySet($data['keySet']); - } - - $operation = $this->serializer->decodeMessage( - new Delete, - $data - ); - break; - default: - $operation = new Write; - $operation->setTable($data['table']); - $operation->setColumns($data['columns']); - - $modifiedData = []; - foreach ($data['values'] as $key => $param) { - $modifiedData[$key] = $this->fieldValue($param); - } - - $list = new ListValue; - $list->setValues($modifiedData); - $values = [$list]; - $operation->setValues($values); - - break; - } - - $setterName = $this->mutationSetters[$type]; - $mutation = new Mutation; - $mutation->$setterName($operation); - $mutations[] = $mutation; - } - return $mutations; - } -} diff --git a/Spanner/src/Connection/IamDatabase.php b/Spanner/src/Connection/IamDatabase.php deleted file mode 100644 index 74d4f27f3aba..000000000000 --- a/Spanner/src/Connection/IamDatabase.php +++ /dev/null @@ -1,65 +0,0 @@ -connection = $connection; - } - - /** - * @param array $args - */ - public function getPolicy(array $args) - { - return $this->connection->getDatabaseIamPolicy($args); - } - - /** - * @param array $args - */ - public function setPolicy(array $args) - { - return $this->connection->setDatabaseIamPolicy($args); - } - - /** - * @param array $args - */ - public function testPermissions(array $args) - { - return $this->connection->testDatabaseIamPermissions($args); - } -} diff --git a/Spanner/src/Connection/IamInstance.php b/Spanner/src/Connection/IamInstance.php deleted file mode 100644 index 29fdf910b2a2..000000000000 --- a/Spanner/src/Connection/IamInstance.php +++ /dev/null @@ -1,65 +0,0 @@ -connection = $connection; - } - - /** - * @param array $args - */ - public function getPolicy(array $args) - { - return $this->connection->getInstanceIamPolicy($args); - } - - /** - * @param array $args - */ - public function setPolicy(array $args) - { - return $this->connection->setInstanceIamPolicy($args); - } - - /** - * @param array $args - */ - public function testPermissions(array $args) - { - return $this->connection->testInstanceIamPermissions($args); - } -} diff --git a/Spanner/src/Connection/LongRunningConnection.php b/Spanner/src/Connection/LongRunningConnection.php deleted file mode 100644 index 529bc2354583..000000000000 --- a/Spanner/src/Connection/LongRunningConnection.php +++ /dev/null @@ -1,75 +0,0 @@ -connection = $connection; - } - - /** - * @param array $args - */ - public function get(array $args) - { - return $this->connection->getOperation($args); - } - - /** - * @param array $args - */ - public function cancel(array $args) - { - return $this->connection->cancelOperation($args); - } - - /** - * @param array $args - */ - public function delete(array $args) - { - return $this->connection->deleteOperation($args); - } - - /** - * @param array $args - */ - public function operations(array $args) - { - return $this->connection->listOperations($args); - } -} diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index 23e4317e0462..255b4d6229f7 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -17,30 +17,54 @@ namespace Google\Cloud\Spanner; +use Closure; +use Google\ApiCore\RetrySettings; +use Google\ApiCore\Serializer; use Google\ApiCore\ApiException; +use Google\ApiCore\OperationResponse; use Google\ApiCore\ValidationException; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Exception\ServiceException; -use Google\Cloud\Core\Iam\Iam; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LROTrait; +use Google\Cloud\Core\Iterator\PageIterator; use Google\Cloud\Core\Retry; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; +use Google\Cloud\Core\RequestHandler; +use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; use Google\Cloud\Spanner\Admin\Database\V1\Database\State; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Spanner\Connection\IamDatabase; +use Google\Cloud\Spanner\Admin\Database\V1\DropDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest; +use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\BatchCreateSessionsRequest; +use Google\Cloud\Spanner\V1\BatchWriteRequest; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\Mutation; +use Google\Cloud\Spanner\V1\Mutation\Delete; +use Google\Cloud\Spanner\V1\Mutation\Write; use Google\Cloud\Spanner\V1\BatchWriteResponse; -use Google\Cloud\Spanner\V1\SpannerClient as GapicSpannerClient; use Google\Cloud\Spanner\V1\TypeCode; +use Google\Protobuf\Duration; +use Google\Protobuf\ListValue; +use Google\Protobuf\Struct; +use Google\Protobuf\Value; use Google\Rpc\Code; +use GuzzleHttp\Promise\PromiseInterface; /** * Represents a Cloud Spanner Database. @@ -49,7 +73,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $database = $spanner->connect('my-instance', 'my-database'); * ``` @@ -58,52 +82,18 @@ * // Databases can also be connected to via an Instance. * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $instance = $spanner->instance('my-instance'); * $database = $instance->database('my-database'); * ``` - * - * @method resumeOperation() { - * Resume a Long Running Operation - * - * Example: - * ``` - * $operation = $database->resumeOperation($operationName); - * ``` - * - * @param string $operationName The Long Running Operation name. - * @param array $info [optional] The operation data. - * @return LongRunningOperation - * } - * @method longRunningOperations() { - * List long running operations. - * - * Example: - * ``` - * $operations = $database->longRunningOperations(); - * ``` - * - * @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 - * } */ class Database { - use LROTrait; use TransactionConfigurationTrait; - use RequestHeaderTrait; + use RequestTrait; + use RequestProcessorTrait; + use ApiHelperTrait; const STATE_CREATING = State::CREATING; const STATE_READY = State::READY; @@ -126,39 +116,13 @@ class Database const TYPE_JSON = TypeCode::JSON; const TYPE_PG_OID = 'pgOid'; - /** - * @var ConnectionInterface - * @internal - */ - private $connection; - - /** - * @var Instance - */ - private $instance; - /** * @var Operation */ private $operation; /** - * @var string - */ - private $projectId; - - /** - * @var string - */ - private $name; - - /** - * @var array - */ - private $info; - - /** - * @var Iam|null + * @var IamManager|null */ private $iam; @@ -168,40 +132,45 @@ class Database private $session; /** - * @var SessionPoolInterface|null + * @var bool */ - private $sessionPool; + private $isRunningTransaction = false; /** - * @var bool + * @var array */ - private $isRunningTransaction = false; + private $directedReadOptions; /** - * @var string|null + * @var bool */ - private $databaseRole; + private $routeToLeader; /** * @var array */ - private $directedReadOptions; + private $defaultQueryOptions; /** - * @var bool + * @var array */ - private $returnInt64AsObject; + private $mutationSetters = [ + 'insert' => 'setInsert', + 'update' => 'setUpdate', + 'insertOrUpdate' => 'setInsertOrUpdate', + 'replace' => 'setReplace', + 'delete' => 'setDelete' + ]; /** * Create an object representing a Database. * - * @param ConnectionInterface $connection The connection to the - * Cloud Spanner Admin API. This object is created by SpannerClient, - * and should not be instantiated outside of this client. + * @internal Database is constructed by the {@see Instance} class. + * + * @param SpannerClient $spannerClient The Spanner client used to interact with the API. + * @param DatabaseAdminClient $databaseAdminClient The database admin client used to interact with the API. + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param Instance $instance The instance in which the database exists. - * @param LongRunningConnectionInterface $lroConnection An implementation - * mapping to methods which handle LRO resolution in the service. - * @param array $lroCallables * @param string $projectId The project ID. * @param string $name The database name or ID. * @param SessionPoolInterface $sessionPool [optional] The session pool @@ -210,35 +179,45 @@ class Database * be returned as a {@see \Google\Cloud\Core\Int64} object for 32 bit * platform compatibility. **Defaults to** false. * @param string $databaseRole The user created database role which creates the session. + * @param string $config [Optional] { + * Configuration options. + * + * @type bool $routeToLeader Enable/disable Leader Aware Routing. + * **Defaults to** `true` (enabled). + * @type array $defaultQueryOptions + * } */ public function __construct( - ConnectionInterface $connection, - Instance $instance, - LongRunningConnectionInterface $lroConnection, - array $lroCallables, - $projectId, - $name, - SessionPoolInterface $sessionPool = null, - $returnInt64AsObject = false, - array $info = [], - $databaseRole = '' + private SpannerClient $spannerClient, + private DatabaseAdminClient $databaseAdminClient, + private Serializer $serializer, + private Instance $instance, + private string $projectId, + private string $name, + private ?SessionPoolInterface $sessionPool = null, + private bool $returnInt64AsObject = false, + private array $info = [], + private string $databaseRole = '', + array $config = [] ) { - $this->connection = $connection; - $this->instance = $instance; - $this->projectId = $projectId; $this->name = $this->fullyQualifiedDatabaseName($name); - $this->sessionPool = $sessionPool; - $this->operation = new Operation($connection, $returnInt64AsObject); - $this->info = $info; + $this->routeToLeader = $config['routeToLeader'] ?? true; + $this->defaultQueryOptions = $config['defaultQueryOptions'] ?? []; + $this->operation = new Operation( + $this->spannerClient, + $serializer, + $returnInt64AsObject, + [ + 'routeToLeader' => $this->routeToLeader, + 'defaultQueryOptions' => $this->defaultQueryOptions + ] + ); if ($this->sessionPool) { $this->sessionPool->setDatabase($this); } - $this->setLroProperties($lroConnection, $lroCallables, $this->name); - $this->databaseRole = $databaseRole; $this->directedReadOptions = $instance->directedReadOptions(); - $this->returnInt64AsObject = $returnInt64AsObject; } /** @@ -320,7 +299,7 @@ public function backups(array $options = []) * eligible to be automatically deleted by Cloud Spanner. * @param array $options [optional] Configuration options. * - * @return LongRunningOperation + * @return OperationResponse */ public function createBackup($name, \DateTimeInterface $expireTime, array $options = []) { @@ -380,9 +359,14 @@ public function info(array $options = []) */ public function reload(array $options = []) { - return $this->info = $this->connection->getDatabase([ - 'name' => $this->name - ] + $options); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['name'] = $this->name; + + $request = $this->serializer->decodeMessage(new GetDatabaseRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->getDatabase($request, $callOptions); + return $this->info = $this->handleResponse($response); } /** @@ -430,22 +414,24 @@ public function exists(array $options = []) * * @type string[] $statements Additional DDL statements. * } - * @return LongRunningOperation + * @return OperationResponse */ public function create(array $options = []) { - $statements = $this->pluck('statements', $options, false) ?: []; - $dialect = $options['databaseDialect'] ?? null; + list($data, $callOptions) = $this->splitOptionalArgs($options); + $dialect = $data['databaseDialect'] ?? null; - $createStatement = $this->getCreateDbStatement($dialect); + $data += [ + 'parent' => $this->instance->name(), + 'createStatement' => $this->getCreateDbStatement($dialect), + 'extraStatements' => $this->pluck('statements', $data, false) ?: [] + ]; - $operation = $this->connection->createDatabase([ - 'instance' => $this->instance->name(), - 'createStatement' => $createStatement, - 'extraStatements' => $statements - ] + $options); + $request = $this->serializer->decodeMessage(new CreateDatabaseRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - return $this->resumeOperation($operation['name'], $operation); + return $this->databaseAdminClient->createDatabase($request, $callOptions) + ->withResultFunction($this->databaseResultFunction()); } /** @@ -462,7 +448,7 @@ public function create(array $options = []) * `projects//instances//backups/`. * @param array $options [optional] Configuration options. * - * @return LongRunningOperation + * @return OperationResponse */ public function restore($backup, array $options = []) { @@ -487,23 +473,30 @@ public function restore($backup, array $options = []) * @type bool $enableDropProtection If `true`, delete operations for Database * and Instance will be blocked. **Defaults to** `false`. * } - * @return LongRunningOperation + * @return OperationResponse */ public function updateDatabase(array $options = []) { + list($data, $callOptions) = $this->splitOptionalArgs($options); $fieldMask = []; - if (isset($options['enableDropProtection'])) { + + if (isset($data['enableDropProtection'])) { $fieldMask[] = 'enable_drop_protection'; } - return $this->connection->updateDatabase([ + $data += [ + 'updateMask' => ['paths' => $fieldMask], 'database' => [ 'name' => $this->name, - 'enableDropProtection' => $options['enableDropProtection'] ?? false, - ], - 'updateMask' => [ - 'paths' => $fieldMask + 'enableDropProtection' => + $this->pluck('enableDropProtection', $data, false) ?: false ] - ] + $options); + ]; + + $request = $this->serializer->decodeMessage(new UpdateDatabaseRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + return $this->databaseAdminClient->updateDatabase($request, $callOptions) + ->withResultFunction($this->databaseResultFunction()); } /** @@ -529,7 +522,7 @@ public function updateDatabase(array $options = []) * * @param string $statement A DDL statements to run against a database. * @param array $options [optional] Configuration options. - * @return LongRunningOperation + * @return OperationResponse */ public function updateDdl($statement, array $options = []) { @@ -564,16 +557,20 @@ public function updateDdl($statement, array $options = []) * * @param string[] $statements A list of DDL statements to run against a database. * @param array $options [optional] Configuration options. - * @return LongRunningOperation + * @return OperationResponse */ public function updateDdlBatch(array $statements, array $options = []) { - $operation = $this->connection->updateDatabaseDdl($options + [ - 'name' => $this->name, - 'statements' => $statements, - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'database' => $this->name, + 'statements' => $statements + ]; + + $request = $this->serializer->decodeMessage(new UpdateDatabaseDdlRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->resumeOperation($operation['name'], $operation); + return $this->databaseAdminClient->updateDatabaseDdl($request, $callOptions); } /** @@ -600,9 +597,13 @@ public function updateDdlBatch(array $statements, array $options = []) */ public function drop(array $options = []) { - $this->connection->dropDatabase($options + [ - 'name' => $this->name - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['database'] = $this->name; + + $request = $this->serializer->decodeMessage(new DropDatabaseRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $this->databaseAdminClient->dropDatabase($request, $callOptions); if ($this->sessionPool) { $this->sessionPool->clear(); @@ -633,9 +634,14 @@ public function drop(array $options = []) */ public function ddl(array $options = []) { - $ddl = $this->connection->getDatabaseDDL($options + [ - 'name' => $this->name - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['database'] = $this->name; + + $request = $this->serializer->decodeMessage(new GetDatabaseDdlRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->getDatabaseDdl($request, $callOptions); + $ddl = $this->handleResponse($response); if (isset($ddl['statements'])) { return $ddl['statements']; @@ -652,13 +658,15 @@ public function ddl(array $options = []) * $iam = $database->iam(); * ``` * - * @return Iam + * @return IamManager */ public function iam() { if (!$this->iam) { - $this->iam = new Iam( - new IamDatabase($this->connection), + $this->iam = new IamManager( + new RequestHandler($this->serializer, [$this->databaseAdminClient]), + $this->serializer, + DatabaseAdminClient::class, $this->name ); } @@ -723,10 +731,6 @@ public function iam() * **Defaults to** `false`. * @type array $sessionOptions Session configuration and request options. * Session labels may be applied using the `labels` key. - * @type array $directedReadOptions Directed read options. - * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions} - * If using the `replicaSelection::type` setting, utilize the constants available in - * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type} to set a value. * } * @return Snapshot * @throws \BadMethodCallException If attempting to call this method within @@ -744,10 +748,6 @@ public function snapshot(array $options = []) ]; $options['transactionOptions'] = $this->configureSnapshotOptions($options); - $options['directedReadOptions'] = $this->configureDirectedReadOptions( - $options, - $this->directedReadOptions ?? [] - ); $session = $this->selectSession( SessionPoolInterface::CONTEXT_READ, @@ -888,8 +888,12 @@ public function transaction(array $options = []) * @param array $options [optional] { * Configuration Options * - * @type int $maxRetries The number of times to attempt to apply the - * operation before failing. **Defaults to ** `10`. + * @type RetrySettings|array $retrySettings { + * Retry configuration options. Currently, only the `maxRetries` option is supported. + * + * @type int $maxRetries The maximum number of retry attempts before the operation fails. + * Defaults to 10. + * } * @type bool $singleUse If true, a Transaction ID will not be allocated * up front. Instead, the transaction will be considered * "single-use", and may be used for only a single operation. Note @@ -911,10 +915,14 @@ public function runTransaction(callable $operation, array $options = []) if ($this->isRunningTransaction) { throw new \BadMethodCallException('Nested transactions are not supported by this client.'); } + $options += ['retrySettings' => ['maxRetries' => self::MAX_RETRIES]]; - $options += [ - 'maxRetries' => self::MAX_RETRIES, - ]; + $retrySettings = $this->pluck('retrySettings', $options); + if ($retrySettings instanceof RetrySettings) { + $maxRetries = $retrySettings->getMaxRetries(); + } else { + $maxRetries = $retrySettings['maxRetries']; + } // There isn't anything configurable here. $options['transactionOptions'] = $this->configureTransactionOptions($options['transactionOptions'] ?? []); @@ -980,7 +988,7 @@ public function runTransaction(callable $operation, array $options = []) return $res; }; - $retry = new Retry($options['maxRetries'], $delayFn); + $retry = new Retry($maxRetries, $delayFn); try { return $retry->execute($transactionFn, [$operation, $session, $options]); @@ -1688,6 +1696,8 @@ public function execute($sql, array $options = []) ); try { + // Unset the internal flag. + unset($options['singleUse']); return $this->operation->execute($session, $sql, $options); } finally { $session->setExpiration(); @@ -1764,12 +1774,24 @@ public function batchWrite(array $mutationGroups, array $options = []) $mutationGroups = array_map(fn ($x) => $x->toArray(), $mutationGroups); + array_walk( + $mutationGroups, + fn(&$x) => $x['mutations'] = $this->parseMutations($x['mutations']) + ); + try { - return $this->connection->batchWrite([ - 'database' => $this->name(), + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ 'session' => $session->name(), 'mutationGroups' => $mutationGroups - ] + $options); + ]; + + $request = $this->serializer->decodeMessage(new BatchWriteRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + + $response = $this->spannerClient->batchWrite($request, $callOptions); + return $this->handleResponse($response); } finally { $this->isRunningTransaction = false; $session->setExpiration(); @@ -2057,6 +2079,8 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] $options = $this->addLarHeader($options, true, $context); try { + // Unset the internal flag. + unset($options['singleUse']); return $this->operation->read($session, $table, $keySet, $columns, $options); } finally { $session->setExpiration(); @@ -2169,33 +2193,200 @@ public function identity() } /** - * Returns the underlying connection. + * Creates a batch of sessions. + * + * @param array $options { + * @type array $sessionTemplate + * @type int $sessionCount + * } + */ + public function batchCreateSessions(array $options) + { + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['database'] = $this->name; + + + $request = $this->serializer->decodeMessage(new BatchCreateSessionsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + + $response = $this->spannerClient->batchCreateSessions($request, $callOptions); + } + + /** + * Delete session asynchronously. * * @access private - * @return ConnectionInterface + * @param array $options { + * @type name The session name to be deleted + * } + * @return PromiseInterface * @experimental */ - public function connection() + public function deleteSessionAsync(array $options) { - return $this->connection; + list($data, $callOptions) = $this->splitOptionalArgs($options); + + $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->spannerClient->deleteSessionAsync($request, $callOptions); } /** - * Represent the class in a more readable and digestable fashion. + * Lists backup operations. * - * @access private - * @codeCoverageIgnore + * @param array $options [optional] { + * Configuration options. + * + * @type int $pageSize + * The maximum number of resources contained in the underlying API + * response. The API may return fewer values in a page, even if + * there are additional values to be retrieved. + * @type int $resultLimit Limit the number of results returned in total. + * **Defaults to** `0` (return all results). + * @type string $pageToken + * A page token is used to specify a page of values to be returned. + * If no page token is specified (the default), the first page + * of values will be returned. Any page token used here must have + * been generated by a previous call to the API. + * } + * + * @return ItemIterator */ - public function __debugInfo() + public function backupOperations(array $options = []) { - return [ - 'connection' => get_class($this->connection), - 'projectId' => $this->projectId, - 'name' => $this->name, - 'instance' => $this->instance, - 'sessionPool' => $this->sessionPool, - 'session' => $this->session, + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['parent'] = $this->instance->name(); + + $resultLimit = $this->pluck('resultLimit', $options, false); + return new ItemIterator( + new PageIterator( + function (array $operation) { + return new OperationResponse( + $operation['name'], + $this->databaseAdminClient->getOperationsClient(), + ); + }, + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListBackupOperationsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->listBackupOperations($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, + [ + 'itemsKey' => 'operations', + 'resultLimit' => $resultLimit + ] + ) + ); + } + + /** + * Create a database from a backup. + * + * @param string $name The database name. + * @param Backup|string $backup The backup to restore, given + * as a Backup instance or a string of the form + * `projects//instances//backups/`. + * @param array $options [optional] Configuration options. + * + * @return OperationResponse + */ + public function createDatabaseFromBackup($name, $backup, array $options = []) + { + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'parent' => $this->instance->name(), + 'databaseId' => $this->databaseIdOnly($name), + 'backup' => $backup instanceof Backup ? $backup->name() : $backup ]; + + $request = $this->serializer->decodeMessage(new RestoreDatabaseRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + return $this->databaseAdminClient->restoreDatabase($request, $callOptions) + ->withResultFunction($this->databaseResultFunction()); + } + + /** + * Lists database operations. + * + * @param array $options [optional] { + * Configuration options. + * + * @type int $pageSize + * The maximum number of resources contained in the underlying API + * response. The API may return fewer values in a page, even if + * there are additional values to be retrieved. + * @type int $resultLimit Limit the number of results returned in total. + * **Defaults to** `0` (return all results). + * @type string $pageToken + * A page token is used to specify a page of values to be returned. + * If no page token is specified (the default), the first page + * of values will be returned. Any page token used here must have + * been generated by a previous call to the API. + * } + * + * @return ItemIterator + */ + public function databaseOperations(array $options = []) + { + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['parent'] = $this->instance->name(); + + $resultLimit = $this->pluck('resultLimit', $options, false); + return new ItemIterator( + new PageIterator( + function (array $operation) { + return new OperationResponse( + $operation['name'], + $this->databaseAdminClient->getOperationsClient(), + ); + }, + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListDatabaseOperationsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); + + $response = $this->databaseAdminClient->listDatabaseOperations($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, + [ + 'itemsKey' => 'operations', + 'resultLimit' => $resultLimit + ] + ) + ); + } + + /** + * Resume a Long Running Operation + * + * Example: + * ``` + * $operation = $spanner->resumeOperation($operationName); + * ``` + * + * @param string $operationName The Long Running Operation name. + * @return OperationResponse + */ + public function resumeOperation($operationName) + { + return new OperationResponse( + $operationName, + $this->databaseAdminClient->getOperationsClient() + ); } /** @@ -2260,10 +2451,10 @@ private function commitInSingleUseTransaction(array $mutations, array $options = */ private function fullyQualifiedDatabaseName($name) { - $instance = InstanceAdminClient::parseName($this->instance->name())['instance']; + $instance = DatabaseAdminClient::parseName($this->instance->name())['instance']; try { - return GapicSpannerClient::databaseName( + return SpannerClient::databaseName( $this->projectId, $instance, $name @@ -2291,4 +2482,152 @@ private function getCreateDbStatement($dialect) return sprintf('CREATE DATABASE `%s`', $databaseId); } + + /** + * Extracts a database id from fully qualified name. + * + * @param string $name The database name or id. + * @return string + */ + private function databaseIdOnly($name) + { + try { + return DatabaseAdminClient::parseName($name)['database']; + } catch (ValidationException $e) { + return $name; + } + } + + private function parseMutations($rawMutations) + { + if (!is_array($rawMutations)) { + return []; + } + + $mutations = []; + foreach ($rawMutations as $mutation) { + $type = array_keys($mutation)[0]; + $data = $mutation[$type]; + + switch ($type) { + case Operation::OP_DELETE: + if (isset($data['keySet'])) { + $data['keySet'] = $this->formatKeySet($data['keySet']); + } + + $operation = $this->serializer->decodeMessage( + new Delete, + $data + ); + break; + default: + $operation = new Write; + $operation->setTable($data['table']); + $operation->setColumns($data['columns']); + + $modifiedData = []; + foreach ($data['values'] as $key => $param) { + $modifiedData[$key] = $this->fieldValue($param); + } + + $list = new ListValue; + $list->setValues($modifiedData); + $values = [$list]; + $operation->setValues($values); + + break; + } + + $setterName = $this->mutationSetters[$type]; + $mutation = new Mutation; + $mutation->$setterName($operation); + $mutations[] = $mutation; + } + return $mutations; + } + + /** + * @param mixed $param + * @return Value + */ + private function fieldValue($param) + { + $field = new Value; + $value = $this->formatValueForApi($param); + + $setter = null; + switch (array_keys($value)[0]) { + case 'string_value': + $setter = 'setStringValue'; + break; + case 'number_value': + $setter = 'setNumberValue'; + break; + case 'bool_value': + $setter = 'setBoolValue'; + break; + case 'null_value': + $setter = 'setNullValue'; + break; + case 'struct_value': + $setter = 'setStructValue'; + $modifiedParams = []; + foreach ($param as $key => $value) { + $modifiedParams[$key] = $this->fieldValue($value); + } + $value = new Struct; + $value->setFields($modifiedParams); + + break; + case 'list_value': + $setter = 'setListValue'; + $modifiedParams = []; + foreach ($param as $item) { + $modifiedParams[] = $this->fieldValue($item); + } + $list = new ListValue; + $list->setValues($modifiedParams); + $value = $list; + + break; + } + + $value = is_array($value) ? current($value) : $value; + if ($setter) { + $field->$setter($value); + } + + return $field; + } + + private function databaseResultFunction(): Closure + { + return function (DatabaseProto $database) { + $name = DatabaseAdminClient::parseName($database->getName()); + return $this->instance->database($name['name'], [ + 'sessionPool' => $this->sessionPool, + 'database' => $this->serializer->encodeMessage($database), + 'databaseRole' => $this->databaseRole, + ]); + }; + } + + /** + * Represent the class in a more readable and digestable fashion. + * + * @access private + * @codeCoverageIgnore + */ + public function __debugInfo() + { + return [ + 'spannerClient' => get_class($this->spannerClient), + 'databaseAdminClient' => get_class($this->databaseAdminClient), + 'projectId' => $this->projectId, + 'name' => $this->name, + 'instance' => $this->instance, + 'sessionPool' => $this->sessionPool, + 'session' => $this->session, + ]; + } } diff --git a/Spanner/src/Date.php b/Spanner/src/Date.php index 10a831995f1e..547ee2b82486 100644 --- a/Spanner/src/Date.php +++ b/Spanner/src/Date.php @@ -25,7 +25,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $date = $spanner->date(new \DateTimeImmutable('1995-02-04')); * ``` diff --git a/Spanner/src/Duration.php b/Spanner/src/Duration.php deleted file mode 100644 index 4fe506ac0880..000000000000 --- a/Spanner/src/Duration.php +++ /dev/null @@ -1,121 +0,0 @@ -duration($seconds, $nanoSeconds); - * ``` - * - * ``` - * // Duration objects can be cast to json-encoded strings. - * echo (string) $duration; - * ``` - */ -class Duration implements ValueInterface -{ - const TYPE = 'DURATION'; - - /** - * @var int - */ - private $seconds; - - /** - * @var int - */ - private $nanos; - - /** - * @param int $seconds The number of seconds in the duration. - * @param int $nanos The number of nanoseconds in the duration. - */ - public function __construct($seconds, $nanos = 0) - { - $this->seconds = $seconds; - $this->nanos = $nanos; - } - - /** - * Get the duration - * - * Example: - * ``` - * $res = $duration->get(); - * ``` - * - * @return array - */ - public function get() - { - return [ - 'seconds' => $this->seconds, - 'nanos' => $this->nanos - ]; - } - - /** - * Get the type. - * - * Example: - * ``` - * echo $duration->type(); - * ``` - * - * @return string - */ - public function type() - { - return self::TYPE; - } - - /** - * Format the value as a string. - * - * Example: - * ``` - * echo $duration->formatAsString(); - * ``` - * - * @return string - */ - public function formatAsString() - { - return json_encode($this->get()); - } - - /** - * Format the value as a string. - * - * @return string - * @access private - */ - public function __toString() - { - return $this->formatAsString(); - } -} diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 2144a6fde12f..7774173a66e4 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -17,22 +17,30 @@ namespace Google\Cloud\Spanner; +use Closure; +use Google\ApiCore\ArrayTrait; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; -use Google\Cloud\Core\ArrayTrait; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; -use Google\Cloud\Core\Iam\Iam; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LROTrait; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; +use Google\Cloud\Core\RequestHandler; +use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; use Google\Cloud\Spanner\Admin\Instance\V1\Instance\State; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceRequest; use Google\Cloud\Spanner\Backup; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Spanner\Connection\IamInstance; use Google\Cloud\Spanner\Session\SessionPoolInterface; +use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; /** * Represents a Cloud Spanner instance @@ -41,50 +49,17 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => $projectId]); * * $instance = $spanner->instance('my-instance'); * ``` - * - * @method resumeOperation() { - * Resume a Long Running Operation - * - * Example: - * ``` - * $operation = $instance->resumeOperation($operationName); - * ``` - * - * @param string $operationName The Long Running Operation name. - * @param array $info [optional] The operation data. - * @return LongRunningOperation - * } - * @method longRunningOperations() { - * List long running operations. - * - * Example: - * ``` - * $operations = $instance->longRunningOperations(); - * ``` - * - * @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 - * } */ class Instance { + use ApiHelperTrait; use ArrayTrait; - use LROTrait; + use RequestTrait; + use RequestProcessorTrait; const STATE_READY = State::READY; const STATE_CREATING = State::CREATING; @@ -92,50 +67,39 @@ class Instance const DEFAULT_NODE_COUNT = 1; /** - * @var ConnectionInterface - * @internal - */ - private $connection; - - /** - * @var string - */ - private $projectId; - - /** - * @var string + * @var IamManager|null */ - private $name; + private $iam; /** - * @var bool + * @var array */ - private $returnInt64AsObject; + private $directedReadOptions; /** * @var array */ - private $info; + private $defaultQueryOptions; /** - * @var Iam|null + * @var bool */ - private $iam; + private $routeToLeader; /** - * @var array + * @var string */ - private $directedReadOptions; + private $projectName; /** * Create an object representing a Cloud Spanner instance. * - * @param ConnectionInterface $connection The connection to the - * Cloud Spanner Admin API. This object is created by SpannerClient, - * and should not be instantiated outside of this client. - * @param LongRunningConnectionInterface $lroConnection An implementation - * mapping to methods which handle LRO resolution in the service. - * @param array $lroCallables + * @internal Instance is constructed by the {@see SpannerClient} class. + * + * @param GapicSpannerClient $spannerClient The spanner client. + * @param InstanceAdminClient $instanceAdminClient The instance admin client. + * @param DatabaseAdminClient $databaseAdminClient The database admin client. + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param string $projectId The project ID. * @param string $name The instance name or ID. * @param bool $returnInt64AsObject [optional] If true, 64 bit integers will be @@ -149,26 +113,26 @@ class Instance * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions} * If using the `replicaSelection::type` setting, utilize the constants available in * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type} to set a value. + * @type bool $routeToLeader Enable/disable Leader Aware Routing. + * **Defaults to** `true` (enabled). * } */ public function __construct( - ConnectionInterface $connection, - LongRunningConnectionInterface $lroConnection, - array $lroCallables, - $projectId, - $name, - $returnInt64AsObject = false, - array $info = [], + private GapicSpannerClient $spannerClient, + private InstanceAdminClient $instanceAdminClient, + private DatabaseAdminClient $databaseAdminClient, + private Serializer $serializer, + private string $projectId, + private string $name, + private bool $returnInt64AsObject = false, + private array $info = [], array $options = [] ) { - $this->connection = $connection; - $this->projectId = $projectId; $this->name = $this->fullyQualifiedInstanceName($name, $projectId); - $this->returnInt64AsObject = $returnInt64AsObject; - $this->info = $info; - - $this->setLroProperties($lroConnection, $lroCallables, $this->name); $this->directedReadOptions = $options['directedReadOptions'] ?? []; + $this->routeToLeader = $options['routeToLeader'] ?? true; + $this->defaultQueryOptions = $options['defaultQueryOptions'] ?? []; + $this->projectName = InstanceAdminClient::projectName($projectId); } /** @@ -234,15 +198,17 @@ public function info(array $options = []) */ public function exists(array $options = []) { + list($data, $callOptions) = $this->splitOptionalArgs($options); try { if ($this->info) { - $this->connection->getInstance([ + $data += [ 'name' => $this->name, - 'projectName' => InstanceAdminClient::projectName( - $this->projectId - ), - 'fieldMask' => ['name'], - ] + $options); + 'fieldMask' => ['paths' => ['name']], + ]; + $request = $this->serializer->decodeMessage(new GetInstanceRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + + $this->instanceAdminClient->getInstance($request, $callOptions); } else { $this->reload($options); } @@ -276,12 +242,28 @@ public function exists(array $options = []) */ public function reload(array $options = []) { - $this->info = $this->connection->getInstance($options + [ - 'name' => $this->name, - 'projectName' => InstanceAdminClient::projectName($this->projectId), - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'name' => $this->name + ]; - return $this->info; + if (isset($data['fieldMask'])) { + $fieldMask = []; + if (is_array($data['fieldMask'])) { + foreach (array_values($data['fieldMask']) as $field) { + $fieldMask[] = $this->serializer::toSnakeCase($field); + } + } else { + $fieldMask[] = $this->serializer::toSnakeCase($data['fieldMask']); + } + $data['fieldMask'] = ['paths' => $fieldMask]; + } + + $request = $this->serializer->decodeMessage(new GetInstanceRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + + $response = $this->instanceAdminClient->getInstance($request, $callOptions); + return $this->info = $this->handleResponse($response); } /** @@ -305,36 +287,34 @@ public function reload(array $options = []) * @type array $labels For more information, see * [Using labels to organize Google Cloud Platform resources](https://cloudplatform.googleblog.com/2015/10/using-labels-to-organize-Google-Cloud-Platform-resources.html). * } - * @return LongRunningOperation + * @return OperationResponse * @throws \InvalidArgumentException * @codingStandardsIgnoreEnd */ public function create(InstanceConfiguration $config, array $options = []) { + list($instance, $callOptions) = $this->splitOptionalArgs($options); $instanceId = InstanceAdminClient::parseName($this->name)['instance']; - $options += [ - 'displayName' => $instanceId, - 'labels' => [], - ]; - - if (isset($options['nodeCount']) && isset($options['processingUnits'])) { + if (isset($instance['nodeCount']) && isset($instance['processingUnits'])) { throw new \InvalidArgumentException("Must only set either `nodeCount` or `processingUnits`"); } - if (empty($options['nodeCount']) && empty($options['processingUnits'])) { - $options['nodeCount'] = self::DEFAULT_NODE_COUNT; + if (empty($instance['nodeCount']) && empty($instance['processingUnits'])) { + $instance['nodeCount'] = self::DEFAULT_NODE_COUNT; } - // This must always be set to CREATING, so overwrite anything else. - $options['state'] = State::CREATING; - - $operation = $this->connection->createInstance([ + $data = [ + 'parent' => InstanceAdminClient::projectName( + $this->projectId + ), 'instanceId' => $instanceId, - 'name' => $this->name, - 'projectName' => InstanceAdminClient::projectName($this->projectId), - 'config' => $config->name() - ] + $options); + 'instance' => $this->createInstanceArray($instance, $config) + ]; + + $request = $this->serializer->decodeMessage(new CreateInstanceRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->resumeOperation($operation['name'], $operation); + return $this->instanceAdminClient->createInstance($request, $callOptions) + ->withResultFunction($this->instanceResultFunction()); } /** @@ -360,7 +340,8 @@ public function state(array $options = []) { $info = $this->info($options); - return (isset($info['state'])) + // @TODO investigate why state is now 0 but in v1 it was unset + return (isset($info['state']) && $info['state'] !== 0) ? $info['state'] : null; } @@ -391,20 +372,28 @@ public function state(array $options = []) * @type array $labels For more information, see * [Using labels to organize Google Cloud Platform resources](https://goo.gl/xmQnxf). * } - * @return LongRunningOperation + * @return OperationResponse * @throws \InvalidArgumentException */ public function update(array $options = []) { + list($instance, $callOptions) = $this->splitOptionalArgs($options); + if (isset($options['nodeCount']) && isset($options['processingUnits'])) { throw new \InvalidArgumentException("Must only set either `nodeCount` or `processingUnits`"); } - $operation = $this->connection->updateInstance([ - 'name' => $this->name, - ] + $options); + $fieldMask = $this->fieldMask($instance); + $data = [ + 'fieldMask' => $fieldMask, + 'instance' => $this->createInstanceArray($instance) + ]; + + $request = $this->serializer->decodeMessage(new UpdateInstanceRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->resumeOperation($operation['name'], $operation); + return $this->instanceAdminClient->updateInstance($request, $callOptions) + ->withResultFunction($this->instanceResultFunction()); } /** @@ -424,9 +413,13 @@ public function update(array $options = []) */ public function delete(array $options = []) { - $this->connection->deleteInstance($options + [ - 'name' => $this->name - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['name'] = $this->name; + + $request = $this->serializer->decodeMessage(new DeleteInstanceRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $this->instanceAdminClient->deleteInstance($request, $callOptions); } /** @@ -449,7 +442,7 @@ public function delete(array $options = []) * @type SessionPoolInterface $sessionPool A pool used to manage * sessions. * } - * @return LongRunningOperation + * @return OperationResponse */ public function createDatabase($name, array $options = []) { @@ -473,22 +466,11 @@ public function createDatabase($name, array $options = []) * `projects//instances//backups/`. * @param array $options [optional] Configuration options. * - * @return LongRunningOperation + * @return OperationResponse */ - public function createDatabaseFromBackup($name, $backup, array $options = []) { - $backup = $backup instanceof Backup - ? $backup->name() - : $backup; - - $operation = $this->connection->restoreDatabase([ - 'instance' => $this->name(), - 'databaseId' => $this->databaseIdOnly($name), - 'backup' => $backup, - ] + $options); - - return $this->resumeOperation($operation['name'], $operation); + return $this->database($name)->createDatabaseFromBackup($name, $backup, $options); } /** @@ -518,16 +500,20 @@ public function createDatabaseFromBackup($name, $backup, array $options = []) public function database($name, array $options = []) { return new Database( - $this->connection, + $this->spannerClient, + $this->databaseAdminClient, + $this->serializer, $this, - $this->lroConnection, - $this->lroCallables, $this->projectId, $name, isset($options['sessionPool']) ? $options['sessionPool'] : null, $this->returnInt64AsObject, isset($options['database']) ? $options['database'] : [], - isset($options['databaseRole']) ? $options['databaseRole'] : '' + isset($options['databaseRole']) ? $options['databaseRole'] : '', + [ + 'routeToLeader' => $this->routeToLeader, + 'defaultQueryOptions' => $this->defaultQueryOptions, + ] ); } @@ -557,14 +543,27 @@ public function database($name, array $options = []) */ public function databases(array $options = []) { - $resultLimit = $this->pluck('resultLimit', $options, false); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['parent'] = $this->name; + + $resultLimit = $this->pluck('resultLimit', $data, false); return new ItemIterator( new PageIterator( function (array $database) { return $this->database($database['name'], ['database' => $database]); }, - [$this->connection, 'listDatabases'], - $options + ['instance' => $this->name], + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListDatabasesRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->listDatabases($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, [ 'itemsKey' => 'databases', 'resultLimit' => $resultLimit @@ -588,10 +587,9 @@ function (array $database) { public function backup($name, array $backup = []) { return new Backup( - $this->connection, + $this->databaseAdminClient, + $this->serializer, $this, - $this->lroConnection, - $this->lroCallables, $this->projectId, $name, $backup @@ -631,6 +629,9 @@ public function backup($name, array $backup = []) */ public function backups(array $options = []) { + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['parent'] = $this->name; + $resultLimit = $this->pluck('resultLimit', $options, false); return new ItemIterator( new PageIterator( @@ -640,8 +641,18 @@ function (array $backup) { $backup ); }, - [$this->connection, 'listBackups'], - $options + ['instance' => $this->name], + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListBackupsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $response = $this->databaseAdminClient->listBackups($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, [ 'itemsKey' => 'backups', 'resultLimit' => $resultLimit @@ -674,24 +685,11 @@ function (array $backup) { * been generated by a previous call to the API. * } * - * @return ItemIterator + * @return ItemIterator */ public function backupOperations(array $options = []) { - $resultLimit = $this->pluck('resultLimit', $options, false); - return new ItemIterator( - new PageIterator( - function (array $operation) { - return $this->resumeOperation($operation['name'], $operation); - }, - [$this->connection, 'listBackupOperations'], - $options + ['instance' => $this->name], - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) - ); + return $this->database($this->name)->backupOperations($options); } /** @@ -718,24 +716,11 @@ function (array $operation) { * been generated by a previous call to the API. * } * - * @return ItemIterator + * @return ItemIterator */ public function databaseOperations(array $options = []) { - $resultLimit = $this->pluck('resultLimit', $options, false); - return new ItemIterator( - new PageIterator( - function (array $operation) { - return $this->resumeOperation($operation['name'], $operation); - }, - [$this->connection, 'listDatabaseOperations'], - $options + ['instance' => $this->name], - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) - ); + return $this->database($this->name)->databaseOperations($options); } /** @@ -746,13 +731,15 @@ function (array $operation) { * $iam = $instance->iam(); * ``` * - * @return Iam + * @return IamManager */ public function iam() { if (!$this->iam) { - $this->iam = new Iam( - new IamInstance($this->connection), + $this->iam = new IamManager( + new RequestHandler($this->serializer, [$this->instanceAdminClient]), + $this->serializer, + InstanceAdminClient::class, $this->name ); } @@ -769,29 +756,10 @@ public function iam() */ private function fullyQualifiedInstanceName($name, $project) { - // try { - return InstanceAdminClient::instanceName( - $project, - $name - ); - // } catch (ValidationException $e) { - // return $name; - // } - } - - /** - * Extracts a database id from fully qualified name. - * - * @param string $name The database name or id. - * @return string - */ - private function databaseIdOnly($name) - { - try { - return DatabaseAdminClient::parseName($name)['database']; - } catch (ValidationException $e) { - return $name; - } + return InstanceAdminClient::instanceName( + $project, + $name + ); } /** @@ -803,7 +771,9 @@ private function databaseIdOnly($name) public function __debugInfo() { return [ - 'connection' => get_class($this->connection), + 'spannerClient' => get_class($this->spannerClient), + 'databaseAdminClient' => get_class($this->databaseAdminClient), + 'instanceAdminClient' => get_class($this->instanceAdminClient), 'projectId' => $this->projectId, 'name' => $this->name, 'info' => $this->info @@ -824,4 +794,73 @@ public function directedReadOptions() { return $this->directedReadOptions; } + + /** + * @param array $instanceArray + * @return array + */ + private function fieldMask(array $instanceArray) + { + $mask = []; + foreach (array_keys($instanceArray) as $key) { + $mask[] = $this->serializer::toSnakeCase($key); + } + return ['paths' => $mask]; + } + + /** + * @param array $instanceArray + * @param InstanceConfiguration $config + * @return array + */ + public function createInstanceArray(array $instanceArray, InstanceConfiguration $config = null) + { + return $instanceArray + [ + 'name' => $this->name, + 'displayName' => InstanceAdminClient::parseName($this->name)['instance'], + 'labels' => [], + 'config' => $config ? $config->name() : '' + ]; + } + + /** + * Resume a Long Running Operation + * + * Example: + * ``` + * $operation = $spanner->resumeOperation($operationName); + * ``` + * + * @param string $operationName The Long Running Operation name. + * @return OperationResponse + */ + public function resumeOperation($operationName) + { + return new OperationResponse( + $operationName, + $this->instanceAdminClient->getOperationsClient() + ); + } + + private function instanceResultFunction(): Closure + { + return function (InstanceProto $result) { + $name = InstanceAdminClient::parseName($result->getName()); + return new self( + $this->spannerClient, + $this->instanceAdminClient, + $this->databaseAdminClient, + $this->serializer, + $this->projectId, + $name['instance'], + $this->returnInt64AsObject, + $this->serialize->encodeMessage($result), + [ + 'directedReadOptions' => $this->directedReadOptions, + 'routeToLeader' => $this->routeToLeader, + 'defaultQueryOptions' => $this->defaultQueryOptions, + ] + ); + }; + } } diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index e38162aad35f..2a010ae2748c 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -17,19 +17,22 @@ namespace Google\Cloud\Spanner; -use Google\Cloud\Core\ArrayTrait; -use Google\Cloud\Core\Exception\NotFoundException; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LROTrait; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\ApiCore\ApiException; +use Google\ApiCore\Serializer; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\ValidationException; +use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig\State; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig\Type; use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Spanner\Connection\LongRunningConnection; -use Google\ApiCore\ValidationException; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; +use Google\Rpc\Code; +use Closure; /** * Represents a Cloud Spanner Instance Configuration. @@ -38,7 +41,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => $projectId]); * * $configuration = $spanner->instanceConfiguration('regional-europe-west'); * ``` @@ -49,78 +52,36 @@ */ class InstanceConfiguration { - use ArrayTrait; - use LROTrait; - - /** - * @var ConnectionInterface - * @internal - */ - private $connection; - - /** - * @var string - */ - private $projectId; + use ApiHelperTrait; + use RequestTrait; + use RequestProcessorTrait; /** * @var string */ private $name; - /** - * @var array - */ - private $info; - /** * Create an instance configuration object. * - * @param ConnectionInterface $connection A service connection for the - * Spanner API. This object is created by SpannerClient, - * and should not be instantiated outside of this client. + * @internal InstanceConfiguration is constructed by the {@see SpannerClient} class. + * + * @param InstanceAdminClient The client library to use for the request + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param string $projectId The current project ID. * @param string $name The configuration name or ID. * @param array $info [optional] A service representation of the * configuration. - * @param LongRunningConnectionInterface $lroConnection An implementation - * mapping to methods which handle LRO resolution in the service. */ public function __construct( - ConnectionInterface $connection, - $projectId, + private InstanceAdminClient $instanceAdminClient, + private Serializer $serializer, + private string $projectId, $name, - array $info = [], - LongRunningConnectionInterface $lroConnection = null + private array $info = [] ) { - $this->connection = $connection; - $this->projectId = $projectId; $this->name = $this->fullyQualifiedConfigName($name, $projectId); $this->info = $info; - $lroConnection = $lroConnection ?: new LongRunningConnection($this->connection); - $instanceConfigFactoryFn = function ($instanceConfig) use ($connection, $projectId, $name, $lroConnection) { - $name = InstanceAdminClient::parseName($instanceConfig['name'])['instance_config']; - return new self( - $connection, - $projectId, - $name, - $instanceConfig, - $lroConnection - ); - }; - $this->setLroProperties( - $lroConnection, - [ - [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceConfigMetadata', - 'callable' => $instanceConfigFactoryFn - ], - [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata', - 'callable' => $instanceConfigFactoryFn - ] - ] - ); } /** @@ -185,8 +146,11 @@ public function exists(array $options = []) { try { $this->reload($options = []); - } catch (NotFoundException $e) { - return false; + } catch (ApiException $e) { + if ($e->getCode() === Code::NOT_FOUND) { + return false; + } + throw $e; } return true; @@ -209,12 +173,19 @@ public function exists(array $options = []) */ public function reload(array $options = []) { - $this->info = $this->connection->getInstanceConfig($options + [ - 'name' => $this->name, - 'projectName' => InstanceAdminClient::projectName($this->projectId), - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += ['name' => $this->name]; + $callOptions = $this->addResourcePrefixHeader( + $callOptions, + InstanceAdminClient::projectName($this->projectId) + ); - return $this->info; + $response = $this->instanceAdminClient->getInstanceConfig( + $this->serializer->decodeMessage(new GetInstanceConfigRequest(), $data), + $callOptions + ); + + return $this->info = $this->handleResponse($response); } /** @@ -247,35 +218,43 @@ public function reload(array $options = []) * @type bool $validateOnly An option to validate, but not actually execute, the request, and provide the same * response. **Defaults to** `false`. * } - * @return LongRunningOperation + * @return OperationResponse * @throws ValidationException * @codingStandardsIgnoreEnd */ public function create(InstanceConfiguration $baseConfig, array $replicas, array $options = []) { - $configId = InstanceAdminClient::parseName($this->name)['instance_config']; + list($data, $callOptions) = $this->splitOptionalArgs($options); + $leaderOptions = $baseConfig->__debugInfo()['info']['leaderOptions'] ?? []; - $options += [ - 'displayName' => $configId, - 'labels' => [], + $validateOnly = $data['validateOnly'] ?? false; + unset($data['validateOnly']); + $data += [ 'replicas' => $replicas, - 'leaderOptions' => $leaderOptions, + 'baseConfig' => $baseConfig->name(), + 'leaderOptions' => $leaderOptions + ]; + $instanceConfig = $this->instanceConfigArray($data); + $requestArray = [ + 'parent' => InstanceAdminClient::projectName($this->projectId), + 'instanceConfigId' => InstanceAdminClient::parseName($this->name)['instance_config'], + 'instanceConfig' => $instanceConfig, + 'validateOnly' => $validateOnly ]; - // Set output parameters to their default values. - $options['state'] = State::CREATING; - $options['configType'] = Type::USER_MANAGED; - $options['optionalReplicas'] = []; - $options['reconciling'] = false; + $request = $this->serializer->decodeMessage( + new CreateInstanceConfigRequest(), + $requestArray + ); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $operation = $this->connection->createInstanceConfig([ - 'instanceConfigId' => $configId, - 'name' => $this->name, - 'projectName' => InstanceAdminClient::projectName($this->projectId), - 'baseConfig' => $baseConfig->name(), - ] + $options); + $operationResponse = $this->instanceAdminClient->createInstanceConfig( + $request, + $callOptions + ); - return $this->resumeOperation($operation['name'], $operation); + return $operationResponse + ->withResultFunction($this->instanceConfigResultFunction()); } /** @@ -302,16 +281,30 @@ public function create(InstanceConfiguration $baseConfig, array $replicas, array * @type bool $validateOnly An option to validate, but not actually execute, the request, and provide the same * response. **Defaults to** `false`. * } - * @return LongRunningOperation + * @return OperationResponse * @throws \InvalidArgumentException */ public function update(array $options = []) { - $operation = $this->connection->updateInstanceConfig([ - 'name' => $this->name, - ] + $options); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $validateOnly = $data['validateOnly'] ?? false; + unset($data['validateOnly']); + $data += ['name' => $this->name]; - return $this->resumeOperation($operation['name'], $operation); + $request = $this->serializer->decodeMessage(new UpdateInstanceConfigRequest(), [ + 'instanceConfig' => $data, + 'updateMask' => $this->fieldMask($data), + 'validateOnly' => $validateOnly + ]); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + + $operationResponse = $this->instanceAdminClient->updateInstanceConfig( + $request, + $callOptions + ); + + return $operationResponse + ->withResultFunction($this->instanceConfigResultFunction()); } /** @@ -332,25 +325,13 @@ public function update(array $options = []) */ public function delete(array $options = []) { - $this->connection->deleteInstanceConfig([ - 'name' => $this->name - ] + $options); - } + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += ['name' => $this->name]; - /** - * A more readable representation of the object. - * - * @codeCoverageIgnore - * @access private - */ - public function __debugInfo() - { - return [ - 'connection' => get_class($this->connection), - 'projectId' => $this->projectId, - 'name' => $this->name, - 'info' => $this->info, - ]; + $this->instanceAdminClient->deleteInstanceConfig( + $this->serializer->decodeMessage(new DeleteInstanceConfigRequest(), $data), + $this->addResourcePrefixHeader($callOptions, $this->name) + ); } /** @@ -371,4 +352,63 @@ private function fullyQualifiedConfigName($name, $projectId) return $name; } } + + /** + * @param array $args + * + * @return array + */ + private function instanceConfigArray(array $args) + { + $configId = InstanceAdminClient::parseName($this->name)['instance_config']; + + return $args += [ + 'name' => $this->name, + 'displayName' => $configId, + 'configType' => Type::USER_MANAGED + ]; + } + + /** + * @param array $instanceArray + * @return array + */ + private function fieldMask(array $instanceArray) + { + $mask = []; + foreach (array_keys($instanceArray) as $key) { + $mask[] = $this->serializer::toSnakeCase($key); + } + return ['paths' => $mask]; + } + + private function instanceConfigResultFunction(): Closure + { + return function (InstanceConfig $result) { + $name = InstanceAdminClient::parseName($result->getName()); + return new self( + $this->instanceAdminClient, + $this->serializer, + $this->projectId, + $name['instance_config'], + $this->serializer->encodeMessage($result) + ); + }; + } + + /** + * A more readable representation of the object. + * + * @codeCoverageIgnore + * @access private + */ + public function __debugInfo() + { + return [ + 'instanceAdminClient' => get_class($this->instanceAdminClient), + 'projectId' => $this->projectId, + 'name' => $this->name, + 'info' => $this->info, + ]; + } } diff --git a/Spanner/src/KeyRange.php b/Spanner/src/KeyRange.php index 727d33819191..c66614fbe91f 100644 --- a/Spanner/src/KeyRange.php +++ b/Spanner/src/KeyRange.php @@ -24,7 +24,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * // Create a KeyRange for all people named Bob, born in 1969. * $start = $spanner->date(new \DateTime('1969-01-01')); diff --git a/Spanner/src/KeySet.php b/Spanner/src/KeySet.php index ca11bceee36d..4aebb92eb526 100644 --- a/Spanner/src/KeySet.php +++ b/Spanner/src/KeySet.php @@ -26,7 +26,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $keySet = $spanner->keySet(); * ``` diff --git a/Spanner/src/MutationTrait.php b/Spanner/src/MutationTrait.php index 51ca50c20789..2361de3b5836 100644 --- a/Spanner/src/MutationTrait.php +++ b/Spanner/src/MutationTrait.php @@ -28,8 +28,6 @@ */ trait MutationTrait { - use ArrayTrait; - /** * @var array */ @@ -334,6 +332,6 @@ private function flattenKeySet(KeySet $keySet) $keys['keys'] = $this->getValueMapper()->encodeValuesAsSimpleType($keys['keys'], true); } - return $this->arrayFilterRemoveNull($keys); + return array_filter($keys, fn ($v) => !is_null($v));; } } diff --git a/Spanner/src/Numeric.php b/Spanner/src/Numeric.php index 63d3a47ab170..f01c01ec32eb 100644 --- a/Spanner/src/Numeric.php +++ b/Spanner/src/Numeric.php @@ -29,7 +29,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $numeric = $spanner->numeric('99999999999999999999999999999999999999.999999999'); * ``` diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index 6c8aeecace91..95a08ddfe068 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -17,14 +17,27 @@ namespace Google\Cloud\Spanner; -use Google\Cloud\Core\ArrayTrait; +use Google\ApiCore\Serializer; +use Google\Cloud\Core\ApiHelperTrait; +use Google\ApiCore\ArrayTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Core\ValidateTrait; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; -use Google\Cloud\Spanner\Connection\ConnectionInterface; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Spanner\V1\SpannerClient as GapicSpannerClient; +use Google\Cloud\Spanner\V1\BeginTransactionRequest; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\CommitRequest; +use Google\Cloud\Spanner\V1\CreateSessionRequest; +use Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; +use Google\Cloud\Spanner\V1\PartitionQueryRequest; +use Google\Cloud\Spanner\V1\PartitionReadRequest; +use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\RollbackRequest; +use Google\Cloud\Spanner\V1\Type; +use Google\Protobuf\Duration; use Google\Rpc\Code; use InvalidArgumentException; @@ -40,7 +53,10 @@ */ class Operation { + use ApiHelperTrait; use ArrayTrait; + use RequestTrait; + use RequestProcessorTrait; use MutationTrait; use TimeTrait; use ValidateTrait; @@ -52,28 +68,44 @@ class Operation const OP_DELETE = 'delete'; /** - * @var ConnectionInterface - * @internal + * @var ValueMapper */ - private $connection; + private $mapper; /** - * @var ValueMapper + * @var bool */ - private $mapper; + private $routeToLeader; /** - * @param ConnectionInterface $connection A connection to Google Cloud - * Spanner. This object is created by SpannerClient, - * and should not be instantiated outside of this client. + * @var array + */ + private $defaultQueryOptions; + + /** + * @param SpannerClient $spannerClient The Spanner client used to make requests. + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param bool $returnInt64AsObject If true, 64 bit integers will be * returned as a {@see \Google\Cloud\Core\Int64} object for 32 bit * platform compatibility. + * @param array $config [optional] { + * Configuration options. + * + * @type bool $routeToLeader Enable/disable Leader Aware Routing. + * **Defaults to** `true` (enabled). + * @type array $defaultQueryOptions + * } */ - public function __construct(ConnectionInterface $connection, $returnInt64AsObject) - { - $this->connection = $connection; + public function __construct( + private SpannerClient $spannerClient, + private Serializer $serializer, + bool $returnInt64AsObject, + $config = [] + ) { $this->mapper = new ValueMapper($returnInt64AsObject); + $this->routeToLeader = $this->pluck('routeToLeader', $config, false) ?: true; + $this->defaultQueryOptions = + $this->pluck('defaultQueryOptions', $config, false) ?: []; } /** @@ -132,18 +164,29 @@ public function commit(Session $session, array $mutations, array $options = []) */ public function commitWithResponse(Session $session, array $mutations, array $options = []) { - $options += [ - 'transactionId' => null + list($data, $callOptions) = $this->splitOptionalArgs($options); + $mutations = $this->serializeMutations($mutations); + $data += [ + 'transactionId' => null, + 'session' => $session->name(), + 'mutations' => $mutations ]; + $data = $this->formatSingleUseTransactionOptions($data); - $res = $this->connection->commit($this->arrayFilterRemoveNull([ - 'mutations' => $mutations, - 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session) - ]) + $options); + $request = $this->serializer->decodeMessage(new CommitRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $time = $this->parseTimeString($res['commitTimestamp']); - return [new Timestamp($time[0], $time[1]), $res]; + $response = $this->spannerClient->commit($request, $callOptions); + $timestamp = $response->getCommitTimestamp(); + + return [ + new Timestamp( + $this->createDateTimeFromSeconds($timestamp->getSeconds()), + $timestamp->getNanos() + ), + $this->handleResponse($response) + ]; } /** @@ -162,11 +205,18 @@ public function rollback(Session $session, $transactionId, array $options = []) if (empty($transactionId)) { throw new InvalidArgumentException('Rollback failed: Transaction not initiated.'); } - $this->connection->rollback([ - 'transactionId' => $transactionId, + + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data = [ 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session) - ] + $options); + 'transactionId' => $transactionId + ]; + + $request = $this->serializer->decodeMessage(new RollbackRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + + $this->spannerClient->rollback($request, $callOptions); } /** @@ -221,7 +271,7 @@ public function execute(Session $session, $sql, array $options = []) $options['resumeToken'] = $resumeToken; } - return $this->connection->executeStreamingSql([ + return $this->executeStreamingSql([ 'sql' => $sql, 'session' => $session->name(), 'database' => $this->getDatabaseNameFromSession($session) @@ -261,7 +311,9 @@ public function executeUpdate( if (!isset($options['transaction']['begin'])) { $options['transaction'] = ['id' => $transaction->id()]; } + $statsItem = $this->pluck('statsItem', $options, false); $res = $this->execute($session, $sql, $options); + if (empty($transaction->id()) && $res->transaction()) { $transaction->setId($res->transaction()->id()); } @@ -276,7 +328,7 @@ public function executeUpdate( ); } - $statsItem = $options['statsItem'] ?? 'rowCountExact'; + $statsItem = $statsItem ?: 'rowCountExact'; return $stats[$statsItem]; } @@ -328,28 +380,19 @@ public function executeUpdateBatch( array $statements, array $options = [] ) { - $stmts = []; - foreach ($statements as $statement) { - if (!isset($statement['sql'])) { - throw new InvalidArgumentException('Each statement must contain a SQL key.'); - } - - $parameters = $this->pluck('parameters', $statement, false) ?: []; - $types = $this->pluck('types', $statement, false) ?: []; - $stmts[] = [ - 'sql' => $statement['sql'] - ] + $this->mapper->formatParamsForExecuteSql($parameters, $types); - } + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['transaction'] = $this->createTransactionSelector($data, $transaction->id()); + $data += [ + 'session' => $session->name(), + 'statements' => $this->formatStatements($statements) + ]; - if (!isset($options['transaction']['begin'])) { - $options['transaction'] = ['id' => $transaction->id()]; - } + $request = $this->serializer->decodeMessage(new ExecuteBatchDmlRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $res = $this->connection->executeBatchDml([ - 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session), - 'statements' => $stmts - ] + $options); + $response = $this->spannerClient->executeBatchDml($request, $callOptions); + $res = $this->handleResponse($response); if (empty($transaction->id())) { // Get the transaction from array of ResultSets. @@ -402,14 +445,7 @@ public function read( array $columns, array $options = [] ) { - $options += [ - 'index' => null, - 'limit' => null, - 'offset' => null, - 'transactionContext' => null - ]; - - $context = $this->pluck('transactionContext', $options); + $context = $this->pluck('transactionContext', $options, false); $call = function ($resumeToken = null, $transaction = null) use ( $table, @@ -425,7 +461,7 @@ public function read( $options['resumeToken'] = $resumeToken; } - return $this->connection->streamingRead([ + return $this->streamingRead([ 'table' => $table, 'session' => $session->name(), 'columns' => $columns, @@ -462,10 +498,11 @@ public function transaction(Session $session, array $options = []) { $options += [ 'singleUse' => false, - 'isRetry' => false, 'requestOptions' => [] ]; + $isRetry = $this->pluck('isRetry', $options, false) ?: false; $transactionTag = $this->pluck('tag', $options, false); + if (isset($transactionTag)) { $options['requestOptions']['transactionTag'] = $transactionTag; } @@ -473,6 +510,10 @@ public function transaction(Session $session, array $options = []) if (!$options['singleUse'] && (!isset($options['begin']) || isset($options['transactionOptions']['partitionedDml'])) ) { + // Single use transactions never calls the beginTransaction API. + // The `singleUse` key creates issue with serializer as BeginTransactionRequest + // does not have this attribute. + unset($options['singleUse']); $res = $this->beginTransaction($session, $options); } else { $res = []; @@ -483,7 +524,7 @@ public function transaction(Session $session, array $options = []) $res, [ 'tag' => $transactionTag, - 'isRetry' => $options['isRetry'], + 'isRetry' => $isRetry, 'transactionOptions' => $options ] ); @@ -506,7 +547,7 @@ public function createTransaction(Session $session, array $res = [], array $opti ]; $options += [ 'tag' => null, - 'transactionOptions' => null + 'transactionOptions' => [] ]; $options['isRetry'] = $options['isRetry'] ?? false; @@ -538,10 +579,6 @@ public function createTransaction(Session $session, array $res = [], array $opti * @type string $className If set, an instance of the given class will * be instantiated. This setting is intended for internal use. * **Defaults to** `Google\Cloud\Spanner\Snapshot`. - * @type array $directedReadOptions Directed read options. - * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions} - * If using the `replicaSelection::type` setting, utilize the constants available in - * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type} to set a value. * } * @return mixed */ @@ -551,14 +588,18 @@ public function snapshot(Session $session, array $options = []) 'singleUse' => false, 'className' => Snapshot::class ]; + $className = $this->pluck('className', $options); if (!$options['singleUse']) { + // Single use transactions never calls the beginTransaction API. + // The `singleUse` key creates issue with serializer as BeginTransactionRequest + // does not have this attribute. + unset($options['singleUse']); $res = $this->beginTransaction($session, $options); } else { $res = []; } - $className = $this->pluck('className', $options); return $this->createSnapshot( $session, $res + $options, @@ -617,13 +658,21 @@ public function createSnapshot(Session $session, array $res = [], $className = S */ public function createSession($databaseName, array $options = []) { - $res = $this->connection->createSession([ + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data = [ 'database' => $databaseName, 'session' => [ 'labels' => $this->pluck('labels', $options, false) ?: [], 'creator_role' => $this->pluck('creator_role', $options, false) ?: '' - ] - ] + $options); + ]]; + + $request = $this->serializer->decodeMessage(new CreateSessionRequest(), $data); + + $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + + $response = $this->spannerClient->createSession($request, $callOptions); + $res = $this->handleResponse($response); return $this->session($res['name']); } @@ -641,13 +690,15 @@ public function createSession($databaseName, array $options = []) */ public function session($sessionName) { - $sessionNameComponents = GapicSpannerClient::parseName($sessionName); + $sessionNameComponents = SpannerClient::parseName($sessionName); return new Session( - $this->connection, + $this->spannerClient, + $this->serializer, $sessionNameComponents['project'], $sessionNameComponents['instance'], $sessionNameComponents['database'], - $sessionNameComponents['session'] + $sessionNameComponents['session'], + ['routeToLeader' => $this->routeToLeader] ); } @@ -692,19 +743,22 @@ public function partitionQuery(Session $session, $transactionId, $sql, array $op { // cache this to pass to the partition instance. $originalOptions = $options; + list($data, $callOptions) = $this->splitOptionalArgs($options); - $parameters = $this->pluck('parameters', $options, false) ?: []; - $types = $this->pluck('types', $options, false) ?: []; - $options += $this->mapper->formatParamsForExecuteSql($parameters, $types); + $data = $this->formatPartitionQueryOptions($data); + $data += [ + 'transaction' => $this->createTransactionSelector($data, $transactionId), + 'session' => $session->name(), + 'sql' => $sql, + 'partitionOptions' => $this->partitionOptions($data) + ]; - $options = $this->partitionOptions($options); + $request = $this->serializer->decodeMessage(new PartitionQueryRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $res = $this->connection->partitionQuery([ - 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session), - 'transactionId' => $transactionId, - 'sql' => $sql - ] + $options); + $response = $this->spannerClient->partitionQuery($request, $callOptions); + $res = $this->handleResponse($response); $partitions = []; foreach ($res['partitions'] as $partition) { @@ -752,17 +806,22 @@ public function partitionRead( ) { // cache this to pass to the partition instance. $originalOptions = $options; - - $options = $this->partitionOptions($options); - - $res = $this->connection->partitionRead([ + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'transaction' => $this->createTransactionSelector($data, $transactionId), 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session), - 'transactionId' => $transactionId, 'table' => $table, 'columns' => $columns, - 'keySet' => $this->flattenKeySet($keySet) - ] + $options); + 'keySet' => $this->formatKeySet($this->flattenKeySet($keySet)), + 'partitionOptions' => $this->partitionOptions($data) + ]; + + $request = $this->serializer->decodeMessage(new PartitionReadRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + + $response = $this->spannerClient->partitionRead($request, $callOptions); + $res = $this->handleResponse($response); $partitions = []; foreach ($res['partitions'] as $partition) { @@ -784,14 +843,12 @@ public function partitionRead( * @param array $options * @return array */ - private function partitionOptions(array $options) + private function partitionOptions(array &$options) { - $options['partitionOptions'] = array_filter([ + return array_filter([ 'partitionSizeBytes' => $this->pluck('partitionSizeBytes', $options, false), 'maxPartitions' => $this->pluck('maxPartitions', $options, false) ]); - - return $options; } /** @@ -806,14 +863,24 @@ private function partitionOptions(array $options) */ private function beginTransaction(Session $session, array $options = []) { - $options += [ - 'transactionOptions' => [] + list($data, $callOptions) = $this->splitOptionalArgs($options); + $transactionOptions = $this->formatTransactionOptions( + $this->pluck('transactionOptions', $data, false) ?: [] + ); + if (isset($transactionOptions['readWrite']) + || isset($transactionOptions['partitionedDml'])) { + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); + } + $data += [ + 'session' => $session->name(), + 'options' => $transactionOptions ]; - return $this->connection->beginTransaction($options + [ - 'session' => $session->name(), - 'database' => $this->getDatabaseNameFromSession($session) - ]); + $request = $this->serializer->decodeMessage(new BeginTransactionRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + + $response = $this->spannerClient->beginTransaction($request, $callOptions); + return $this->handleResponse($response); } /** @@ -848,6 +915,265 @@ private function getDatabaseNameFromSession(Session $session) return $session->info()['databaseName']; } + /** + * Serialize the mutations. + * + * @param array $mutations + * @return array + */ + private function serializeMutations(array $mutations) + { + $serializedMutations = []; + if (is_array($mutations)) { + foreach ($mutations as $mutation) { + $type = array_keys($mutation)[0]; + $data = $mutation[$type]; + + switch ($type) { + case Operation::OP_DELETE: + if (isset($data['keySet'])) { + $data['keySet'] = $this->formatKeySet($data['keySet']); + } + break; + default: + $modifiedData = array_map([$this, 'formatValueForApi'], $data['values']); + $data['values'] = [['values' => $modifiedData]]; + + break; + } + + $serializedMutations[] = [$type => $data]; + } + } + + return $serializedMutations; + } + + /** + * @param array $keySet + * @return array Formatted keyset + */ + private function formatKeySet(array $keySet) + { + $keys = $this->pluck('keys', $keySet, false); + if ($keys) { + $keySet['keys'] = array_map( + fn ($key) => $this->formatListForApi((array) $key), + $keys + ); + } + + if (isset($keySet['ranges'])) { + $keySet['ranges'] = array_map(function ($rangeItem) { + return array_map([$this, 'formatListForApi'], $rangeItem); + }, $keySet['ranges']); + + if (empty($keySet['ranges'])) { + unset($keySet['ranges']); + } + } + + return $keySet; + } + + /** + * Format statements. + * + * @param array $statements + * @return array + */ + private function formatStatements(array $statements) + { + $result = []; + foreach ($statements as $statement) { + if (!isset($statement['sql'])) { + throw new InvalidArgumentException('Each statement must contain a SQL key.'); + } + + $parameters = $this->pluck('parameters', $statement, false) ?: []; + $types = $this->pluck('types', $statement, false) ?: []; + $mappedStatement = [ + 'sql' => $statement['sql'] + ] + $this->mapper->formatParamsForExecuteSql($parameters, $types); + + $result[] = $this->formatSqlParams($mappedStatement); + } + return $result; + } + + /** + * @param array $args + * @return array + */ + private function formatSqlParams(array $args) + { + $params = $this->pluck('params', $args); + if ($params) { + $modifiedParams = array_map([$this, 'formatValueForApi'], $params); + $args['params'] = ['fields' => $modifiedParams]; + } + + return $args; + } + + /** + * @param array $args + * @param ?string $transactionId + * + * @return array + */ + private function createTransactionSelector(array &$args, ?string $transactionId = null) + { + $transactionSelector = []; + if (isset($args['transaction'])) { + $transactionSelector = $this->pluck('transaction', $args); + + if (isset($transactionSelector['singleUse'])) { + $transactionSelector['singleUse'] = + $this->formatTransactionOptions($transactionSelector['singleUse']); + } + + if (isset($transactionSelector['begin'])) { + $transactionSelector['begin'] = + $this->formatTransactionOptions($transactionSelector['begin']); + } + } elseif ($transactionId) { + $transactionSelector = ['id' => $transactionId]; + } + + return $transactionSelector; + } + + /** + * @param array $data + * + * @return array + */ + private function createQueryOptions(array $args) + { + $queryOptions = $this->pluck('queryOptions', $args, false) ?: []; + // Query options precedence is query-level, then environment-level, then client-level. + $envQueryOptimizerVersion = getenv('SPANNER_OPTIMIZER_VERSION'); + $envQueryOptimizerStatisticsPackage = getenv('SPANNER_OPTIMIZER_STATISTICS_PACKAGE'); + if (!empty($envQueryOptimizerVersion)) { + $queryOptions += ['optimizerVersion' => $envQueryOptimizerVersion]; + } + if (!empty($envQueryOptimizerStatisticsPackage)) { + $queryOptions += ['optimizerStatisticsPackage' => $envQueryOptimizerStatisticsPackage]; + } + $queryOptions += $this->defaultQueryOptions ?: []; + return $queryOptions; + } + + /** + * @param array $transactionOptions + * @return array + */ + private function formatTransactionOptions(array $transactionOptions) + { + if (isset($transactionOptions['readOnly'])) { + $ro = $transactionOptions['readOnly']; + if (isset($ro['minReadTimestamp'])) { + $ro['minReadTimestamp'] = $this->formatTimestampForApi($ro['minReadTimestamp']); + } + + if (isset($ro['readTimestamp'])) { + $ro['readTimestamp'] = $this->formatTimestampForApi($ro['readTimestamp']); + } + + $transactionOptions['readOnly'] = $ro; + } + + return $transactionOptions; + } + + /** + * @param array $args + * @return \Generator + */ + private function executeStreamingSql(array $args) + { + list($data, $callOptions) = $this->splitOptionalArgs($args); + $data = $this->formatSqlParams($data); + $data['transaction'] = $this->createTransactionSelector($data); + $data['queryOptions'] = $this->createQueryOptions($data); + $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); + $databaseName = $this->pluck('database', $data); + + $request = $this->serializer->decodeMessage(new ExecuteSqlRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); + + $response = $this->spannerClient->executeStreamingSql($request, $callOptions); + return $this->handleResponse($response); + } + + /** + * @param array $args + * @return \Generator + */ + private function streamingRead(array $args) + { + list($data, $callOptions) = $this->splitOptionalArgs($args); + $data['keySet']= $this->formatKeySet($this->pluck('keySet', $data)); + $data['transaction'] = $this->createTransactionSelector($data); + $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); + $databaseName = $this->pluck('database', $data); + + $request = $this->serializer->decodeMessage(new ReadRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); + + $response = $this->spannerClient->streamingRead($request, $callOptions); + return $this->handleResponse($response); + } + + /** + * @param array $args + * @return array + */ + private function formatSingleUseTransactionOptions(array $args) + { + // Internal flag, need to unset before passing to serializer + unset($args['singleUse']); + if (isset($args['singleUseTransaction'])) { + $args['singleUseTransaction'] = ['readWrite' => []]; + // request ignores singleUseTransaction even if the transactionId is set to null + unset($args['transactionId']); + } + return $args; + } + + /** + * @param array $args + * @param string $transactionId + * + * @return array + */ + private function formatPartitionQueryOptions(array $args) + { + $parameters = $this->pluck('parameters', $args, false) ?: []; + $types = $this->pluck('types', $args, false) ?: []; + $args += $this->mapper->formatParamsForExecuteSql($parameters, $types); + $args = $this->formatSqlParams($args); + return $args; + } + + /** + * Conditionally unset the LAR header. + * + * @param array $args Request arguments. + * @param bool $value Whether to set or unset the LAR header. + * @return array + */ + private function conditionallyUnsetLarHeader( + array $args, + bool $value = true + ) { + if (!$value) { + unset($args['headers'][$this->larHeader]); + } + return $args; + } + /** * Represent the class in a more readable and digestable fashion. * @@ -857,7 +1183,7 @@ private function getDatabaseNameFromSession(Session $session) public function __debugInfo() { return [ - 'connection' => get_class($this->connection), + 'spannerClient' => get_class($this->spannerClient), ]; } } diff --git a/Spanner/src/PgJsonb.php b/Spanner/src/PgJsonb.php index 5788e7dbe8e4..6118eb2bdae7 100644 --- a/Spanner/src/PgJsonb.php +++ b/Spanner/src/PgJsonb.php @@ -30,7 +30,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $pgJsonb = $spanner->pgJsonb('{}'); * ``` */ diff --git a/Spanner/src/PgNumeric.php b/Spanner/src/PgNumeric.php index 8128d3208502..4bc820ca9c35 100644 --- a/Spanner/src/PgNumeric.php +++ b/Spanner/src/PgNumeric.php @@ -31,7 +31,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $pgNumeric = $spanner->pgNumeric('99999999999999999999999999999999999999.000000999999999'); * ``` diff --git a/Spanner/src/PgOid.php b/Spanner/src/PgOid.php index 8a3d0b9a1ced..047474e1151f 100644 --- a/Spanner/src/PgOid.php +++ b/Spanner/src/PgOid.php @@ -28,7 +28,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $pgOid = $spanner->pgOid('123'); * ``` */ diff --git a/Spanner/src/RequestHeaderTrait.php b/Spanner/src/RequestTrait.php similarity index 57% rename from Spanner/src/RequestHeaderTrait.php rename to Spanner/src/RequestTrait.php index 456402c38e1c..63aa5ba9c86c 100644 --- a/Spanner/src/RequestHeaderTrait.php +++ b/Spanner/src/RequestTrait.php @@ -17,17 +17,32 @@ namespace Google\Cloud\Spanner; +use Google\Cloud\Spanner\Admin\Database\V1\Backup; +use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupMetadata; +use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupMetadata; +use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseMetadata; +use Google\Cloud\Spanner\Admin\Database\V1\Database; +use Google\Cloud\Spanner\Admin\Database\V1\OptimizeRestoredDatabaseMetadata; +use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseMetadata; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlMetadata; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigMetadata; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceMetadata; +use Google\Cloud\Spanner\Admin\Instance\V1\Instance; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigMetadata; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceMetadata; use Google\Cloud\Spanner\Session\SessionPoolInterface; +use Google\Protobuf\GPBEmpty; /** - * Shared functionality for request headers. + * Shared functionality for Spanner requests. * * @internal */ -trait RequestHeaderTrait +trait RequestTrait { - private static $larHeader = 'x-goog-spanner-route-to-leader'; - private static $resourcePrefixHeader = 'google-cloud-resource-prefix'; + private $larHeader = 'x-goog-spanner-route-to-leader'; + private $resourcePrefixHeader = 'google-cloud-resource-prefix'; /** * Add the `x-goog-spanner-route-to-leader` header value to the request. @@ -47,24 +62,7 @@ private function addLarHeader( } // If value is true and context is READWRITE, set LAR header. if ($context === SessionPoolInterface::CONTEXT_READWRITE) { - $args['headers'][self::$larHeader] = ['true']; - } - return $args; - } - - /** - * Conditionally unset the LAR header. - * - * @param array $args Request arguments. - * @param bool $value Whether to set or unset the LAR header. - * @return array - */ - private function conditionallyUnsetLarHeader( - array $args, - bool $value = true - ) { - if (!$value) { - unset($args['headers'][self::$larHeader]); + $args['headers'][$this->larHeader] = ['true']; } return $args; } @@ -78,7 +76,7 @@ private function conditionallyUnsetLarHeader( */ private function addResourcePrefixHeader(array $args, string $value) { - $args['headers'][self::$resourcePrefixHeader] = [$value]; + $args['headers'][$this->resourcePrefixHeader] = [$value]; return $args; } } diff --git a/Spanner/src/Result.php b/Spanner/src/Result.php index b335804a2331..f5b9c6d5ae53 100644 --- a/Spanner/src/Result.php +++ b/Spanner/src/Result.php @@ -17,6 +17,7 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\RetrySettings; use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Core\ExponentialBackoff; use Google\Cloud\Spanner\Session\Session; @@ -31,7 +32,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $database = $spanner->connect('my-instance', 'my-database'); * $result = $database->execute('SELECT * FROM Posts'); @@ -132,8 +133,12 @@ class Result implements \IteratorAggregate * @param callable $call A callable, yielding a generator filled with results. * @param string $transactionContext The transaction's context. * @param ValueMapper $mapper Maps values. - * @param int $retries Number of attempts to resume a broken stream, assuming - * a resume token is present. **Defaults to** 3. + * @param ?RetrySettings $retrySettings { + * Retry configuration options. Currently, only the `maxRetries` option is supported. + * + * @type int $maxRetries The maximum number of retry attempts before the operation fails. + * Defaults to 3. + * } */ public function __construct( Operation $operation, @@ -141,14 +146,14 @@ public function __construct( callable $call, $transactionContext, ValueMapper $mapper, - $retries = 3 + ?RetrySettings $retrySettings = null ) { $this->operation = $operation; $this->session = $session; $this->call = $call; $this->transactionContext = $transactionContext; $this->mapper = $mapper; - $this->retries = $retries; + $this->retries = isset($retrySettings) ? $retrySettings->getMaxRetries() : 3; $this->createGenerator(); } diff --git a/Spanner/src/Session/CacheSessionPool.php b/Spanner/src/Session/CacheSessionPool.php index ec4a04c8e210..d70411121d8d 100644 --- a/Spanner/src/Session/CacheSessionPool.php +++ b/Spanner/src/Session/CacheSessionPool.php @@ -83,7 +83,7 @@ * use Google\Cloud\Spanner\Session\CacheSessionPool; * use Symfony\Component\Cache\Adapter\FilesystemAdapter; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $cache = new FilesystemAdapter(); * $sessionPool = new CacheSessionPool($cache); * @@ -111,7 +111,7 @@ * use Google\Cloud\Spanner\Session\CacheSessionPool; * use Symfony\Component\Cache\Adapter\FilesystemAdapter; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $cache = new FilesystemAdapter(); * $sessionPool = new CacheSessionPool($cache, [ * 'databaseRole' => 'Reader' @@ -690,8 +690,7 @@ private function createSessions($count) // @see https://github.com/googleapis/google-cloud-php/pull/2342#discussion_r327925546 while ($count > $created) { try { - $res = $this->database->connection()->batchCreateSessions([ - 'database' => $this->database->name(), + $res = $this->database->batchCreateSessions([ 'sessionTemplate' => [ 'labels' => isset($this->config['labels']) ? $this->config['labels'] : [], 'creator_role' => isset($this->config['databaseRole']) ? $this->config['databaseRole'] : '' @@ -873,11 +872,9 @@ private function deleteSessions(array $sessions, $waitForPromises = false) { $this->deleteCalls = []; foreach ($sessions as $session) { - $this->deleteCalls[] = $this->database->connection() - ->deleteSessionAsync([ - 'name' => $session['name'], - 'database' => $this->database->name() - ]); + $this->deleteCalls[] = $this->database->deleteSessionAsync([ + 'name' => $session['name'] + ]); } if ($waitForPromises && !empty($this->deleteCalls)) { diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index be909d6c7892..395658a194f3 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -17,71 +17,59 @@ namespace Google\Cloud\Spanner\Session; +use Google\ApiCore\ArrayTrait; +use Google\ApiCore\Serializer; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Spanner\V1\SpannerClient; +use Google\Cloud\Spanner\Database; +use Google\Cloud\Spanner\RequestTrait; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\GetSessionRequest; +use Google\Cloud\Spanner\V1\Client\SpannerClient; /** * Represents and manages a single Cloud Spanner session. */ class Session { - /** - * @var ConnectionInterface - * @internal - */ - private $connection; + use ApiHelperTrait; + use ArrayTrait; + use RequestTrait; /** - * @var string - */ - private $projectId; - - /** - * @var string - */ - private $instance; - - /** - * @var string - */ - private $database; - - /** - * @var string - */ - private $databaseName; - - /** - * @var string + * @var int|null */ - private $name; + private $expiration; /** - * @var int|null + * @var bool */ - private $expiration; + private $routeToLeader; /** - * @param ConnectionInterface $connection A connection to Cloud Spanner. - * This object is created by SpannerClient, - * and should not be instantiated outside of this client. + * @internal Session is constructed by the {@see Database} class. + * + * @param Serializer $serializer The serializer instance to encode/decode messages. * @param string $projectId The project ID. * @param string $instance The instance name. * @param string $database The database name. * @param string $name The session name. + * @param array $config [optional] { + * Configuration options. + * + * @type bool $routeToLeader Enable/disable Leader Aware Routing. + * **Defaults to** `true` (enabled). + * } */ public function __construct( - ConnectionInterface $connection, - $projectId, - $instance, - $database, - $name + private SpannerClient $spannerClient, + private Serializer $serializer, + private $projectId, + private $instance, + private $database, + private $name, + $config = [] ) { - $this->connection = $connection; - $this->projectId = $projectId; - $this->instance = $instance; - $this->database = $database; $this->databaseName = SpannerClient::databaseName( $projectId, $instance, @@ -93,6 +81,7 @@ public function __construct( $database, $name ); + $this->routeToLeader = $this->pluck('routeToLeader', $config, false) ?? true; } /** @@ -120,16 +109,21 @@ public function info() */ public function exists(array $options = []) { + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += [ + 'name' => $this->name() + ]; + try { - $this->connection->getSession($options + [ - 'name' => $this->name(), - 'database' => $this->databaseName - ]); + $request = $this->serializer->decodeMessage(new GetSessionRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->databaseName); + $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - return true; + $this->spannerClient->getSession($request, $callOptions); } catch (NotFoundException $e) { return false; } + return true; } /** @@ -140,10 +134,15 @@ public function exists(array $options = []) */ public function delete(array $options = []) { - $this->connection->deleteSession($options + [ - 'name' => $this->name(), - 'database' => $this->databaseName - ]); + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data = [ + 'name' => $this->name() + ]; + + $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->databaseName); + + $this->spannerClient->deleteSession($request, $callOptions); } /** @@ -188,7 +187,7 @@ public function expiration() public function __debugInfo() { return [ - 'connection' => get_class($this->connection), + 'spannerClient' => get_class($this->spannerClient), 'projectId' => $this->projectId, 'instance' => $this->instance, 'database' => $this->database, diff --git a/Spanner/src/Snapshot.php b/Spanner/src/Snapshot.php index a6d6081104f7..5b9c004d0e50 100644 --- a/Spanner/src/Snapshot.php +++ b/Spanner/src/Snapshot.php @@ -30,7 +30,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $database = $spanner->connect('my-instance', 'my-database'); * $transaction = $database->snapshot(); diff --git a/Spanner/src/SnapshotTrait.php b/Spanner/src/SnapshotTrait.php index b01850b6f277..b566cfcb957d 100644 --- a/Spanner/src/SnapshotTrait.php +++ b/Spanner/src/SnapshotTrait.php @@ -17,6 +17,7 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\ArrayTrait; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Timestamp; @@ -26,6 +27,7 @@ */ trait SnapshotTrait { + use ArrayTrait; use TransactionalReadTrait; /** @@ -65,9 +67,9 @@ private function initialize( throw new \InvalidArgumentException('$options.readTimestamp must be an instance of Timestamp.'); } - $this->transactionId = $options['id'] ?: null; - $this->readTimestamp = $options['readTimestamp']; - $this->type = $options['id'] + $this->transactionId = $this->pluck('id', $options) ?: null; + $this->readTimestamp = $this->pluck('readTimestamp', $options) ?: null; + $this->type = $this->transactionId ? self::TYPE_PRE_ALLOCATED : self::TYPE_SINGLE_USE; diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 9c0d4dcab944..9bfb449604aa 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -17,30 +17,34 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\ClientOptionsTrait; +use Google\ApiCore\CredentialsWrapper; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\Serializer; +use Google\ApiCore\ValidationException; use Google\Auth\FetchAuthTokenInterface; -use Google\Cloud\Core\ArrayTrait; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\ClientTrait; use Google\Cloud\Core\Exception\GoogleException; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; -use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LROTrait; use Google\Cloud\Core\ValidateTrait; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigOperationsRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesRequest; use Google\Cloud\Spanner\Batch\BatchClient; -use Google\Cloud\Spanner\Connection\Grpc; -use Google\Cloud\Spanner\Connection\LongRunningConnection; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; -use Google\Cloud\Spanner\V1\SpannerClient as GapicSpannerClient; +use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\Protobuf\Duration; use Psr\Cache\CacheItemPoolInterface; use Psr\Http\StreamInterface; -use Google\ApiCore\ValidationException; /** * Cloud Spanner is a highly scalable, transactional, managed, NewSQL @@ -59,7 +63,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * ``` * * ``` @@ -70,14 +74,15 @@ * // `9010` is used as an example only. * putenv('SPANNER_EMULATOR_HOST=localhost:9010'); * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * ``` * * ``` * use Google\Cloud\Spanner\SpannerClient; * use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; * - * $directedOptions = [ + * $config = [ + * 'projectId' => 'my-project', * 'directedReadOptions' => [ * 'includeReplicas' => [ * 'replicaSelections' => [ @@ -90,46 +95,43 @@ * ] * ] * ]; - * $spanner = new SpannerClient($directedOptions); + * $spanner = new SpannerClient($config); * ``` * * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $config = ['routeToLeader' => false]; + * $config = ['projectId' => 'my-project', 'routeToLeader' => false]; * $spanner = new SpannerClient($config); * ``` - * - * @method resumeOperation() { - * Resume a Long Running Operation - * - * Example: - * ``` - * $operation = $spanner->resumeOperation($operationName); - * ``` - * - * @param string $operationName The Long Running Operation name. - * @param array $info [optional] The operation data. - * @return LongRunningOperation - * } */ class SpannerClient { - use ArrayTrait; + use ApiHelperTrait; + use ClientOptionsTrait; use ClientTrait; - use LROTrait; use ValidateTrait; + use RequestTrait; + use RequestProcessorTrait; const VERSION = '1.89.0'; const FULL_CONTROL_SCOPE = 'https://www.googleapis.com/auth/spanner.data'; const ADMIN_SCOPE = 'https://www.googleapis.com/auth/spanner.admin'; + private GapicSpannerClient $spannerClient; + private InstanceAdminClient $instanceAdminClient; + private DatabaseAdminClient $databaseAdminClient; + + /** + * @var Serializer + */ + private Serializer $serializer; + /** - * @var Connection\ConnectionInterface - * @internal + * @var string */ - protected $connection; + private $projectId; /** * @var bool @@ -141,6 +143,16 @@ class SpannerClient */ private $directedReadOptions; + /** + * @var bool + */ + private $routeToLeader; + + /** + * @var array + */ + private $defaultQueryOptions; + /** * Create a Spanner client. Please note that this client requires * [the gRPC extension](https://cloud.google.com/php/grpc). @@ -148,31 +160,22 @@ class SpannerClient * @param array $config [optional] { * Configuration Options. * + * @type string $projectId The Google Cloud project ID. * @type string $apiEndpoint A hostname with optional port to use in * place of the service's default endpoint. * @type string $projectId The project ID from the Google Developer's * Console. - * @type CacheItemPoolInterface $authCache A cache for storing access + * @type CacheItemPoolInterface $credentialsConfig.authCache A cache for storing access * tokens. **Defaults to** a simple in memory implementation. - * @type array $authCacheOptions Cache configuration options. - * @type callable $authHttpHandler A handler used to deliver Psr7 + * @type array $credentialsConfig.authCacheOptions Cache configuration options. + * @type callable $credentialsConfig.authHttpHandler A handler used to deliver Psr7 * requests specifically for authentication. - * @type FetchAuthTokenInterface $credentialsFetcher A credentials - * fetcher instance. - * @type callable $httpHandler A handler used to deliver Psr7 requests. + * @type callable $transportConfig.rest.httpHandler A handler used to deliver Psr7 requests. * Only valid for requests sent over REST. - * @type array $keyFile The contents of the service account credentials - * .json file retrieved from the Google Developer's Console. - * Ex: `json_decode(file_get_contents($path), true)`. - * @type string $keyFilePath The full path to your service account - * credentials .json file retrieved from the Google Developers - * Console. - * @type float $requestTimeout Seconds to wait before timing out the - * request. **Defaults to** `0` with REST and `60` with gRPC. - * @type int $retries Number of retries for a failed request. Used only - * with default backoff strategy. **Defaults to** `3`. - * @type array $scopes Scopes to be used for the request. - * @type string $quotaProject Specifies a user project to bill for + * @type string|array|FetchAuthTokenInterface|CredentialsWrapper $credentials + * The credentials to be used by the client to authorize API calls. + * @type array $credentialsConfig.scopes Scopes to be used for the request. + * @type string $credentialsConfig.quotaProject Specifies a user project to bill for * access charges associated with the request. * @type bool $returnInt64AsObject If true, 64 bit integers will be * returned as a {@see \Google\Cloud\Core\Int64} object for 32 bit @@ -192,10 +195,6 @@ class SpannerClient * query execution. Executing a SQL statement with an invalid * optimizer version will fail with a syntax error * (`INVALID_ARGUMENT`) status. - * @type bool $useDiscreteBackoffs `false`: use default backoff strategy - * (retry every failed request up to `retries` times). - * `true`: use discrete backoff settings based on called method name. - * **Defaults to** `false`. * @type array $directedReadOptions Directed read options. * {@see \Google\Cloud\Spanner\V1\DirectedReadOptions} * If using the `replicaSelection::type` setting, utilize the constants available in @@ -210,11 +209,8 @@ public function __construct(array $config = []) $emulatorHost = getenv('SPANNER_EMULATOR_HOST'); $this->requireGrpc(); + $scopes = [self::FULL_CONTROL_SCOPE, self::ADMIN_SCOPE]; $config += [ - 'scopes' => [ - self::FULL_CONTROL_SCOPE, - self::ADMIN_SCOPE - ], 'returnInt64AsObject' => false, 'projectIdRequired' => true, 'hasEmulator' => (bool) $emulatorHost, @@ -222,64 +218,54 @@ public function __construct(array $config = []) 'queryOptions' => [] ]; - if (!empty($config['useDiscreteBackoffs'])) { - $config = array_merge_recursive($config, [ - 'retries' => 0, - 'grpcOptions' => [ - 'retrySettings' => [], - ], - ]); - } - - $this->connection = new Grpc($this->configureAuthentication($config)); $this->returnInt64AsObject = $config['returnInt64AsObject']; - - $this->setLroProperties(new LongRunningConnection($this->connection), [ - [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.UpdateInstanceMetadata', - 'callable' => function ($instance) { - $name = InstanceAdminClient::parseName($instance['name'])['instance']; - return $this->instance($name, $instance); - } - ], [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CreateDatabaseMetadata', - 'callable' => function ($database) { - $databaseNameComponents = DatabaseAdminClient::parseName($database['name']); - $instanceName = $databaseNameComponents['instance']; - $databaseName = $databaseNameComponents['database']; - - $instance = $this->instance($instanceName); - return $instance->database($databaseName); - } - ], [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata', - 'callable' => function ($database) { - $databaseNameComponents = DatabaseAdminClient::parseName($database['name']); - $instanceName = $databaseNameComponents['instance']; - $databaseName = $databaseNameComponents['database']; - - $instance = $this->instance($instanceName); - return $instance->database($databaseName); - } - ],[ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceMetadata', - 'callable' => function ($instance) { - $name = InstanceAdminClient::parseName($instance['name'])['instance']; - return $this->instance($name, $instance); - } - ], [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata', - 'callable' => function ($backup) { - $backupNameComponents = DatabaseAdminClient::parseName($backup['name']); - $instanceName = $backupNameComponents['instance']; - - $instance = $this->instance($instanceName); - return $instance->backup($backup['name'], $backup); - } - ] + $this->directedReadOptions = $config['directedReadOptions'] ?? []; + $this->routeToLeader = $config['routeToLeader'] ?? true; + $this->defaultQueryOptions = $config['queryOptions']; + + // Configure GAPIC client options + $config = $this->buildClientOptions($config); + if (isset($config['credentialsConfig']['scopes'])) { + $config['credentialsConfig']['scopes'] = array_merge( + $config['credentialsConfig']['scopes'], + $scopes + ); + } else { + $config['credentialsConfig']['scopes'] = $scopes; + } + $config['credentials'] = $this->createCredentialsWrapper( + $config['credentials'], + $config['credentialsConfig'], + $config['universeDomain'] + ); + $this->projectId = $this->detectProjectId($config); + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } ]); - $this->directedReadOptions = $config['directedReadOptions'] ?? []; + // Adds some defaults + // gccl needs to be present for handwritten clients + $clientConfig = $config += [ + 'libName' => 'gccl', + 'serializer' => $this->serializer, + ]; + $this->spannerClient = $config['gapicSpannerClient'] ?? new GapicSpannerClient($clientConfig); + $this->instanceAdminClient = $config['gapicSpannerInstanceAdminClient'] + ?? new InstanceAdminClient($clientConfig); + $this->databaseAdminClient = $config['gapicSpannerDatabaseAdminClient'] + ?? new DatabaseAdminClient($clientConfig); + $this->projectName = InstanceAdminClient::projectName($this->projectId); } /** @@ -311,8 +297,13 @@ public function __construct(array $config = []) public function batch($instanceId, $databaseId, array $options = []) { $operation = new Operation( - $this->connection, - $this->returnInt64AsObject + $this->spannerClient, + $this->serializer, + $this->returnInt64AsObject, + [ + 'routeToLeader' => $this->routeToLeader, + 'defaultQueryOptions' => $this->defaultQueryOptions + ] ); return new BatchClient( @@ -379,7 +370,7 @@ public function batch($instanceId, $databaseId, array $options = []) * @type bool $validateOnly An option to validate, but not actually execute, the request, and provide the same * response. **Defaults to** `false`. * } - * @return LongRunningOperation + * @return OperationResponse * @throws ValidationException */ public function createInstanceConfiguration(InstanceConfiguration $baseConfig, $name, array $replicas, array $options = []) @@ -414,15 +405,27 @@ public function createInstanceConfiguration(InstanceConfiguration $baseConfig, $ */ public function instanceConfigurations(array $options = []) { - $resultLimit = $this->pluck('resultLimit', $options, false) ?: 0; + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data['parent'] = $this->projectName; + $resultLimit = $this->pluck('resultLimit', $options, false) ?: 0; return new ItemIterator( new PageIterator( function (array $config) { return $this->instanceConfiguration($config['name'], $config); }, - [$this->connection, 'listInstanceConfigs'], - ['projectName' => InstanceAdminClient::projectName($this->projectId)] + $options, + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListInstanceConfigsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + + $response = $this->instanceAdminClient->listInstanceConfigs($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, [ 'itemsKey' => 'instanceConfigs', 'resultLimit' => $resultLimit @@ -456,11 +459,11 @@ function (array $config) { public function instanceConfiguration($name, array $options = []) { return new InstanceConfiguration( - $this->connection, + $this->instanceAdminClient, + $this->serializer, $this->projectId, $name, - $options, - $this->lroConnection + $options ); } @@ -488,18 +491,30 @@ public function instanceConfiguration($name, array $options = []) * been generated by a previous call to the API. * } * - * @return ItemIterator + * @return ItemIterator */ public function instanceConfigOperations(array $options = []) { + list($data, $callOptions) = $this->splitOptionalArgs($options); $resultLimit = $this->pluck('resultLimit', $options, false); + $data['parent'] = $this->projectName; return new ItemIterator( new PageIterator( - function (array $operation) { - return $this->resumeOperation($operation['name'], $operation); + function (OperationResponse $operation) { + return $operation; + }, + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListInstanceConfigOperationsRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + + $response = $this->instanceAdminClient->listInstanceConfigOperations($request, $callOptions); + return $this->handleResponse($response); }, - [$this->connection, 'listInstanceConfigOperations'], - ['projectName' => InstanceAdminClient::projectName($this->projectId)] + $options, + $callOptions, [ 'itemsKey' => 'operations', 'resultLimit' => $resultLimit @@ -529,7 +544,7 @@ function (array $operation) { * @type array $labels For more information, see * [Using labels to organize Google Cloud Platform resources](https://cloudplatform.googleblog.com/2015/10/using-labels-to-organize-Google-Cloud-Platform-resources.html). * } - * @return LongRunningOperation + * @return OperationResponse * @codingStandardsIgnoreEnd */ public function createInstance(InstanceConfiguration $config, $name, array $options = []) @@ -552,14 +567,19 @@ public function createInstance(InstanceConfiguration $config, $name, array $opti public function instance($name, array $instance = []) { return new Instance( - $this->connection, - $this->lroConnection, - $this->lroCallables, + $this->spannerClient, + $this->instanceAdminClient, + $this->databaseAdminClient, + $this->serializer, $this->projectId, $name, $this->returnInt64AsObject, $instance, - ['directedReadOptions' => $this->directedReadOptions] + [ + 'directedReadOptions' => $this->directedReadOptions, + 'routeToLeader' => $this->routeToLeader, + 'defaultQueryOptions' => $this->defaultQueryOptions + ] ); } @@ -591,19 +611,28 @@ public function instance($name, array $instance = []) */ public function instances(array $options = []) { - $options += [ - 'filter' => null - ]; + list($data, $callOptions) = $this->splitOptionalArgs($options); + $data += ['filter' => '', 'parent' => $this->projectName]; - $resultLimit = $this->pluck('resultLimit', $options, false); + $resultLimit = $this->pluck('resultLimit', $data, false); return new ItemIterator( new PageIterator( function (array $instance) { $name = InstanceAdminClient::parseName($instance['name'])['instance']; return $this->instance($name, $instance); }, - [$this->connection, 'listInstances'], - ['projectName' => InstanceAdminClient::projectName($this->projectId)] + $options, + function ($callOptions) use ($data) { + if (isset($callOptions['pageToken'])) { + $data['pageToken'] = $callOptions['pageToken']; + } + + $request = $this->serializer->decodeMessage(new ListInstancesRequest(), $data); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + + $response = $this->instanceAdminClient->listInstances($request, $callOptions); + return $this->handleResponse($response); + }, + $callOptions, [ 'itemsKey' => 'instances', 'resultLimit' => $resultLimit @@ -650,6 +679,25 @@ public function connect($instance, $name, array $options = []) return $database; } + /** + * Resume a Long Running Operation + * + * Example: + * ``` + * $operation = $spanner->resumeOperation($operationName); + * ``` + * + * @param string $operationName The Long Running Operation name. + * @return OperationResponse + */ + public function resumeOperation($operationName) + { + return new OperationResponse( + $operationName, + $this->databaseAdminClient->getOperationsClient() + ); + } + /** * Create a new KeySet object * @@ -870,7 +918,7 @@ public function int64($value) */ public function duration($seconds, $nanos = 0) { - return new Duration($seconds, $nanos); + return new Duration(['seconds' => $seconds, 'nanos' => $nanos]); } /** diff --git a/Spanner/src/StructType.php b/Spanner/src/StructType.php index 0f5fcf756f92..8f0de6c1c3b8 100644 --- a/Spanner/src/StructType.php +++ b/Spanner/src/StructType.php @@ -26,7 +26,7 @@ * use Google\Cloud\Spanner\Database; * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $database = $spanner->connect('my-instance', 'my-database'); * * $res = $database->execute('SELECT @userStruct.firstName, @userStruct.lastName', [ diff --git a/Spanner/src/StructValue.php b/Spanner/src/StructValue.php index 7cde1caffa00..77227acf2137 100644 --- a/Spanner/src/StructValue.php +++ b/Spanner/src/StructValue.php @@ -40,7 +40,7 @@ * use Google\Cloud\Spanner\StructType; * use Google\Cloud\Spanner\StructValue; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * $database = $spanner->connect('my-instance', 'my-database'); * * $res = $database->execute('SELECT * FROM UNNEST(ARRAY(SELECT @structParam))', [ diff --git a/Spanner/src/Timestamp.php b/Spanner/src/Timestamp.php index 01b239881bb4..8d53b271bf0b 100644 --- a/Spanner/src/Timestamp.php +++ b/Spanner/src/Timestamp.php @@ -32,7 +32,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $timestamp = $spanner->timestamp(new \DateTime('2003-02-05 11:15:02.421827Z')); * ``` diff --git a/Spanner/src/Transaction.php b/Spanner/src/Transaction.php index 0db1e79cef97..a6ee6fe2012b 100644 --- a/Spanner/src/Transaction.php +++ b/Spanner/src/Transaction.php @@ -17,10 +17,12 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; +use Google\Protobuf\Duration; /** * Manages interaction with Cloud Spanner inside a Transaction. @@ -47,7 +49,7 @@ * ``` * use Google\Cloud\Spanner\SpannerClient; * - * $spanner = new SpannerClient(); + * $spanner = new SpannerClient(['projectId' => 'my-project']); * * $database = $spanner->connect('my-instance', 'my-database'); * @@ -78,13 +80,6 @@ class Transaction implements TransactionalReadInterface */ private $mutations = []; - /** - * @var bool - */ - private $isRetry = false; - - private ValueMapper $mapper; - /** * @param Operation $operation The Operation instance. * @param Session $session The session to use for spanner interactions. @@ -103,19 +98,14 @@ class Transaction implements TransactionalReadInterface * @throws \InvalidArgumentException if a tag is specified on a single-use transaction. */ public function __construct( - Operation $operation, - Session $session, - $transactionId = null, - $isRetry = false, - $tag = null, - $options = [], - $mapper = null + private Operation $operation, + private Session $session, + private ?string $transactionId = null, + private bool $isRetry = false, + ?string $tag = null, + array $options = [], + private ?ValueMapper $mapper = null ) { - $this->operation = $operation; - $this->session = $session; - $this->transactionId = $transactionId; - $this->isRetry = $isRetry; - $this->type = ($transactionId || isset($options['begin'])) ? self::TYPE_PRE_ALLOCATED : self::TYPE_SINGLE_USE; @@ -125,13 +115,10 @@ public function __construct( "Cannot set a transaction tag on a single-use transaction." ); } - $this->tag = $tag; $this->context = SessionPoolInterface::CONTEXT_READWRITE; $this->options = $options; - if (!is_null($mapper)) { - $this->mapper = $mapper; - } + $this->tag =$tag; } /** diff --git a/Spanner/src/TransactionConfigurationTrait.php b/Spanner/src/TransactionConfigurationTrait.php index 642279a39b2d..88e4ac39f862 100644 --- a/Spanner/src/TransactionConfigurationTrait.php +++ b/Spanner/src/TransactionConfigurationTrait.php @@ -17,7 +17,7 @@ namespace Google\Cloud\Spanner; -use Google\Cloud\Core\ArrayTrait; +use Google\ApiCore\ArrayTrait; use Google\Cloud\Spanner\Session\SessionPoolInterface; /** @@ -203,11 +203,6 @@ private function configureSnapshotOptions(array &$options, array $previous = []) 'readTimestamp' ]; - $durationFields = [ - 'exactStaleness', - 'maxStaleness' - ]; - foreach ($timestampFields as $tsf) { if (isset($transactionOptions['readOnly'][$tsf]) && !isset($previousOptions[$tsf])) { $field = $transactionOptions['readOnly'][$tsf]; @@ -223,21 +218,6 @@ private function configureSnapshotOptions(array &$options, array $previous = []) } } - foreach ($durationFields as $df) { - if (isset($transactionOptions['readOnly'][$df]) && !isset($previousOptions[$df])) { - $field = $transactionOptions['readOnly'][$df]; - if (!($field instanceof Duration)) { - throw new \BadMethodCallException(sprintf( - 'Read Only Transaction Configuration Field %s must be an instance of `%s`.', - $df, - Duration::class - )); - } - - $transactionOptions['readOnly'][$df] = $field->get(); - } - } - return $transactionOptions; } } diff --git a/Spanner/src/TransactionalReadTrait.php b/Spanner/src/TransactionalReadTrait.php index 7186eb5e670c..5a64826fa3bd 100644 --- a/Spanner/src/TransactionalReadTrait.php +++ b/Spanner/src/TransactionalReadTrait.php @@ -26,57 +26,57 @@ trait TransactionalReadTrait { use TransactionConfigurationTrait; - use RequestHeaderTrait; + use RequestTrait; /** * @var Operation */ - private $operation; + private Operation $operation; /** * @var Session */ - private $session; + private Session $session; /** * @var string */ - private $transactionId; + private ?string $transactionId; /** * @var string */ - private $context; + private string $context; /** * @var int */ - private $type; + private int $type; /** * @var int */ - private $state = 0; // TransactionalReadInterface::STATE_ACTIVE + private int $state = TransactionalReadInterface::STATE_ACTIVE; /** * @var array */ - private $options = []; + private array $options = []; /** * @var int */ - private $seqno = 1; + private int $seqno = 1; /** * @var string */ - private $tag = null; + private ?string $tag = null; /** * @var array */ - private $directedReadOptions = []; + private array $directedReadOptions = []; /** * Run a query. @@ -304,8 +304,11 @@ public function execute($sql, array $options = []) ); $options = $this->addLarHeader($options, true, $this->context); + // Unsetting the internal flag + unset($options['singleUse']); $result = $this->operation->execute($this->session, $sql, $options); + if (empty($this->id()) && $result->transaction()) { $this->setId($result->transaction()->id()); } @@ -365,7 +368,6 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] $options['transactionId'] = $this->transactionId; } $options['transactionType'] = $this->context; - $options += $this->options; $selector = $this->transactionSelector($options, $this->options); $options['transaction'] = $selector[0]; diff --git a/Spanner/src/V1/BatchCreateSessionsRequest.php b/Spanner/src/V1/BatchCreateSessionsRequest.php index 29eccb7710cc..f76a48fe2104 100644 --- a/Spanner/src/V1/BatchCreateSessionsRequest.php +++ b/Spanner/src/V1/BatchCreateSessionsRequest.php @@ -21,13 +21,13 @@ class BatchCreateSessionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * Parameters to be applied to each created session. * * Generated from protobuf field .google.spanner.v1.Session session_template = 2; */ - private $session_template = null; + protected $session_template = null; /** * Required. The number of sessions to be created in this batch call. * The API may return fewer than the requested number of sessions. If a @@ -38,7 +38,7 @@ class BatchCreateSessionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 session_count = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $session_count = 0; + protected $session_count = 0; /** * @param string $database Required. The database in which the new sessions are created. Please see diff --git a/Spanner/src/V1/BatchWriteRequest.php b/Spanner/src/V1/BatchWriteRequest.php index 514f30f40654..d5467dbbe1e9 100644 --- a/Spanner/src/V1/BatchWriteRequest.php +++ b/Spanner/src/V1/BatchWriteRequest.php @@ -20,13 +20,13 @@ class BatchWriteRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Common options for this request. * * Generated from protobuf field .google.spanner.v1.RequestOptions request_options = 3; */ - private $request_options = null; + protected $request_options = null; /** * Required. The groups of mutations to be applied. * @@ -48,7 +48,7 @@ class BatchWriteRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool exclude_txn_from_change_streams = 5 [(.google.api.field_behavior) = OPTIONAL]; */ - private $exclude_txn_from_change_streams = false; + protected $exclude_txn_from_change_streams = false; /** * @param string $session Required. The session in which the batch request is to be run. Please see diff --git a/Spanner/src/V1/BatchWriteRequest/MutationGroup.php b/Spanner/src/V1/BatchWriteRequest/MutationGroup.php index 22d04c7f4b91..5a73e8bd786a 100644 --- a/Spanner/src/V1/BatchWriteRequest/MutationGroup.php +++ b/Spanner/src/V1/BatchWriteRequest/MutationGroup.php @@ -67,6 +67,4 @@ public function setMutations($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(MutationGroup::class, \Google\Cloud\Spanner\V1\BatchWriteRequest_MutationGroup::class); diff --git a/Spanner/src/V1/BatchWriteResponse.php b/Spanner/src/V1/BatchWriteResponse.php index f5aab11e6437..efcb9bc2ddb9 100644 --- a/Spanner/src/V1/BatchWriteResponse.php +++ b/Spanner/src/V1/BatchWriteResponse.php @@ -27,14 +27,14 @@ class BatchWriteResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.rpc.Status status = 2; */ - private $status = null; + protected $status = null; /** * The commit timestamp of the transaction that applied this batch. * Present if `status` is `OK`, absent otherwise. * * Generated from protobuf field .google.protobuf.Timestamp commit_timestamp = 3; */ - private $commit_timestamp = null; + protected $commit_timestamp = null; /** * Constructor. diff --git a/Spanner/src/V1/BeginTransactionRequest.php b/Spanner/src/V1/BeginTransactionRequest.php index 8e65dbc9c804..43f37b91fc39 100644 --- a/Spanner/src/V1/BeginTransactionRequest.php +++ b/Spanner/src/V1/BeginTransactionRequest.php @@ -21,13 +21,13 @@ class BeginTransactionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Required. Options for the new transaction. * * Generated from protobuf field .google.spanner.v1.TransactionOptions options = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $options = null; + protected $options = null; /** * Common options for this request. * Priority is ignored for this request. Setting the priority in this diff --git a/Spanner/src/V1/Client/SpannerClient.php b/Spanner/src/V1/Client/SpannerClient.php index 599de9deb370..159136a71c2b 100644 --- a/Spanner/src/V1/Client/SpannerClient.php +++ b/Spanner/src/V1/Client/SpannerClient.php @@ -1,6 +1,6 @@ int64 mutation_count = 1; */ - private $mutation_count = 0; + protected $mutation_count = 0; /** * Constructor. @@ -89,6 +89,4 @@ public function setMutationCount($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(CommitStats::class, \Google\Cloud\Spanner\V1\CommitResponse_CommitStats::class); diff --git a/Spanner/src/V1/CommitResponse_CommitStats.php b/Spanner/src/V1/CommitResponse_CommitStats.php deleted file mode 100644 index fb24d1a6a1f7..000000000000 --- a/Spanner/src/V1/CommitResponse_CommitStats.php +++ /dev/null @@ -1,16 +0,0 @@ -string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * Required. The session to create. * * Generated from protobuf field .google.spanner.v1.Session session = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $session = null; + protected $session = null; /** * @param string $database Required. The database in which the new session is created. Please see diff --git a/Spanner/src/V1/DeleteSessionRequest.php b/Spanner/src/V1/DeleteSessionRequest.php index 40c61a458a81..0bf9497d1ccc 100644 --- a/Spanner/src/V1/DeleteSessionRequest.php +++ b/Spanner/src/V1/DeleteSessionRequest.php @@ -20,7 +20,7 @@ class DeleteSessionRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the session to delete. Please see diff --git a/Spanner/src/V1/DirectedReadOptions/ExcludeReplicas.php b/Spanner/src/V1/DirectedReadOptions/ExcludeReplicas.php index 913c6fba2724..c9be8f05e62c 100644 --- a/Spanner/src/V1/DirectedReadOptions/ExcludeReplicas.php +++ b/Spanner/src/V1/DirectedReadOptions/ExcludeReplicas.php @@ -66,6 +66,4 @@ public function setReplicaSelections($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(ExcludeReplicas::class, \Google\Cloud\Spanner\V1\DirectedReadOptions_ExcludeReplicas::class); diff --git a/Spanner/src/V1/DirectedReadOptions/IncludeReplicas.php b/Spanner/src/V1/DirectedReadOptions/IncludeReplicas.php index d9219874a781..de69639290ae 100644 --- a/Spanner/src/V1/DirectedReadOptions/IncludeReplicas.php +++ b/Spanner/src/V1/DirectedReadOptions/IncludeReplicas.php @@ -29,7 +29,7 @@ class IncludeReplicas extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool auto_failover_disabled = 2; */ - private $auto_failover_disabled = false; + protected $auto_failover_disabled = false; /** * Constructor. @@ -108,6 +108,4 @@ public function setAutoFailoverDisabled($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(IncludeReplicas::class, \Google\Cloud\Spanner\V1\DirectedReadOptions_IncludeReplicas::class); diff --git a/Spanner/src/V1/DirectedReadOptions/ReplicaSelection.php b/Spanner/src/V1/DirectedReadOptions/ReplicaSelection.php index 837065c7c621..0115f982f1ef 100644 --- a/Spanner/src/V1/DirectedReadOptions/ReplicaSelection.php +++ b/Spanner/src/V1/DirectedReadOptions/ReplicaSelection.php @@ -34,13 +34,13 @@ class ReplicaSelection extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string location = 1; */ - private $location = ''; + protected $location = ''; /** * The type of replica. * * Generated from protobuf field .google.spanner.v1.DirectedReadOptions.ReplicaSelection.Type type = 2; */ - private $type = 0; + protected $type = 0; /** * Constructor. @@ -113,6 +113,4 @@ public function setType($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(ReplicaSelection::class, \Google\Cloud\Spanner\V1\DirectedReadOptions_ReplicaSelection::class); diff --git a/Spanner/src/V1/DirectedReadOptions/ReplicaSelection/Type.php b/Spanner/src/V1/DirectedReadOptions/ReplicaSelection/Type.php index 94b7d304ad1b..39d4c5781d6c 100644 --- a/Spanner/src/V1/DirectedReadOptions/ReplicaSelection/Type.php +++ b/Spanner/src/V1/DirectedReadOptions/ReplicaSelection/Type.php @@ -59,6 +59,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Type::class, \Google\Cloud\Spanner\V1\DirectedReadOptions_ReplicaSelection_Type::class); diff --git a/Spanner/src/V1/ExecuteBatchDmlRequest.php b/Spanner/src/V1/ExecuteBatchDmlRequest.php index f41278e12801..ce0243d0d79d 100644 --- a/Spanner/src/V1/ExecuteBatchDmlRequest.php +++ b/Spanner/src/V1/ExecuteBatchDmlRequest.php @@ -20,7 +20,7 @@ class ExecuteBatchDmlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Required. The transaction to use. Must be a read-write transaction. * To protect against replays, single-use transactions are not supported. The @@ -29,7 +29,7 @@ class ExecuteBatchDmlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.TransactionSelector transaction = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $transaction = null; + protected $transaction = null; /** * Required. The list of statements to execute in this batch. Statements are * executed serially, such that the effects of statement `i` are visible to @@ -51,13 +51,13 @@ class ExecuteBatchDmlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int64 seqno = 4 [(.google.api.field_behavior) = REQUIRED]; */ - private $seqno = 0; + protected $seqno = 0; /** * Common options for this request. * * Generated from protobuf field .google.spanner.v1.RequestOptions request_options = 5; */ - private $request_options = null; + protected $request_options = null; /** * Constructor. diff --git a/Spanner/src/V1/ExecuteBatchDmlRequest/Statement.php b/Spanner/src/V1/ExecuteBatchDmlRequest/Statement.php index 169686a5f930..cd8aa6d8066e 100644 --- a/Spanner/src/V1/ExecuteBatchDmlRequest/Statement.php +++ b/Spanner/src/V1/ExecuteBatchDmlRequest/Statement.php @@ -20,7 +20,7 @@ class Statement extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string sql = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $sql = ''; + protected $sql = ''; /** * Parameter names and values that bind to placeholders in the DML string. * A parameter placeholder consists of the `@` character followed by the @@ -33,7 +33,7 @@ class Statement extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct params = 2; */ - private $params = null; + protected $params = null; /** * It is not always possible for Cloud Spanner to infer the right SQL type * from a JSON value. For example, values of type `BYTES` and values @@ -203,6 +203,4 @@ public function setParamTypes($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Statement::class, \Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest_Statement::class); diff --git a/Spanner/src/V1/ExecuteBatchDmlRequest_Statement.php b/Spanner/src/V1/ExecuteBatchDmlRequest_Statement.php deleted file mode 100644 index 0e0a5ea61dc8..000000000000 --- a/Spanner/src/V1/ExecuteBatchDmlRequest_Statement.php +++ /dev/null @@ -1,16 +0,0 @@ -string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * The transaction to use. * For queries, if none is provided, the default is a temporary read-only @@ -33,13 +33,13 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.TransactionSelector transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * Required. The SQL string. * * Generated from protobuf field string sql = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $sql = ''; + protected $sql = ''; /** * Parameter names and values that bind to placeholders in the SQL string. * A parameter placeholder consists of the `@` character followed by the @@ -53,7 +53,7 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct params = 4; */ - private $params = null; + protected $params = null; /** * It is not always possible for Cloud Spanner to infer the right SQL type * from a JSON value. For example, values of type `BYTES` and values @@ -77,7 +77,7 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes resume_token = 6; */ - private $resume_token = ''; + protected $resume_token = ''; /** * Used to control the amount of debugging information returned in * [ResultSetStats][google.spanner.v1.ResultSetStats]. If @@ -88,7 +88,7 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.ExecuteSqlRequest.QueryMode query_mode = 7; */ - private $query_mode = 0; + protected $query_mode = 0; /** * If present, results will be restricted to the specified partition * previously created using PartitionQuery(). There must be an exact @@ -97,7 +97,7 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes partition_token = 8; */ - private $partition_token = ''; + protected $partition_token = ''; /** * A per-transaction sequence number used to identify this request. This field * makes each request idempotent such that if the request is received multiple @@ -110,25 +110,25 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int64 seqno = 9; */ - private $seqno = 0; + protected $seqno = 0; /** * Query optimizer configuration to use for the given query. * * Generated from protobuf field .google.spanner.v1.ExecuteSqlRequest.QueryOptions query_options = 10; */ - private $query_options = null; + protected $query_options = null; /** * Common options for this request. * * Generated from protobuf field .google.spanner.v1.RequestOptions request_options = 11; */ - private $request_options = null; + protected $request_options = null; /** * Directed read options for this request. * * Generated from protobuf field .google.spanner.v1.DirectedReadOptions directed_read_options = 15; */ - private $directed_read_options = null; + protected $directed_read_options = null; /** * If this is for a partitioned query and this field is set to `true`, the * request is executed with Spanner Data Boost independent compute resources. @@ -137,7 +137,7 @@ class ExecuteSqlRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool data_boost_enabled = 16; */ - private $data_boost_enabled = false; + protected $data_boost_enabled = false; /** * Constructor. diff --git a/Spanner/src/V1/ExecuteSqlRequest/QueryMode.php b/Spanner/src/V1/ExecuteSqlRequest/QueryMode.php index 43c3ed48f97b..bfd9e0a4d034 100644 --- a/Spanner/src/V1/ExecuteSqlRequest/QueryMode.php +++ b/Spanner/src/V1/ExecuteSqlRequest/QueryMode.php @@ -79,6 +79,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(QueryMode::class, \Google\Cloud\Spanner\V1\ExecuteSqlRequest_QueryMode::class); diff --git a/Spanner/src/V1/ExecuteSqlRequest/QueryOptions.php b/Spanner/src/V1/ExecuteSqlRequest/QueryOptions.php index a96e6b519184..ad1c411054c4 100644 --- a/Spanner/src/V1/ExecuteSqlRequest/QueryOptions.php +++ b/Spanner/src/V1/ExecuteSqlRequest/QueryOptions.php @@ -35,7 +35,7 @@ class QueryOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string optimizer_version = 1; */ - private $optimizer_version = ''; + protected $optimizer_version = ''; /** * An option to control the selection of optimizer statistics package. * This parameter allows individual queries to use a different query @@ -58,7 +58,7 @@ class QueryOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string optimizer_statistics_package = 2; */ - private $optimizer_statistics_package = ''; + protected $optimizer_statistics_package = ''; /** * Constructor. @@ -227,6 +227,4 @@ public function setOptimizerStatisticsPackage($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(QueryOptions::class, \Google\Cloud\Spanner\V1\ExecuteSqlRequest_QueryOptions::class); diff --git a/Spanner/src/V1/ExecuteSqlRequest_QueryMode.php b/Spanner/src/V1/ExecuteSqlRequest_QueryMode.php deleted file mode 100644 index 839429aafee0..000000000000 --- a/Spanner/src/V1/ExecuteSqlRequest_QueryMode.php +++ /dev/null @@ -1,16 +0,0 @@ -string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $name = ''; + protected $name = ''; /** * @param string $name Required. The name of the session to retrieve. Please see diff --git a/Spanner/src/V1/KeySet.php b/Spanner/src/V1/KeySet.php index a956a41f8513..29121208ba59 100644 --- a/Spanner/src/V1/KeySet.php +++ b/Spanner/src/V1/KeySet.php @@ -43,7 +43,7 @@ class KeySet extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool all = 3; */ - private $all = false; + protected $all = false; /** * Constructor. diff --git a/Spanner/src/V1/ListSessionsRequest.php b/Spanner/src/V1/ListSessionsRequest.php index e7329c5e4965..d3fd205fce8d 100644 --- a/Spanner/src/V1/ListSessionsRequest.php +++ b/Spanner/src/V1/ListSessionsRequest.php @@ -20,14 +20,14 @@ class ListSessionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string database = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $database = ''; + protected $database = ''; /** * Number of sessions to be returned in the response. If 0 or less, defaults * to the server's maximum allowed page size. * * Generated from protobuf field int32 page_size = 2; */ - private $page_size = 0; + protected $page_size = 0; /** * If non-empty, `page_token` should contain a * [next_page_token][google.spanner.v1.ListSessionsResponse.next_page_token] @@ -36,7 +36,7 @@ class ListSessionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string page_token = 3; */ - private $page_token = ''; + protected $page_token = ''; /** * An expression for filtering the results of the request. Filter rules are * case insensitive. The fields eligible for filtering are: @@ -48,7 +48,7 @@ class ListSessionsRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string filter = 4; */ - private $filter = ''; + protected $filter = ''; /** * @param string $database Required. The database in which to list sessions. Please see diff --git a/Spanner/src/V1/ListSessionsResponse.php b/Spanner/src/V1/ListSessionsResponse.php index 7edc5e274f64..0d8cff32a984 100644 --- a/Spanner/src/V1/ListSessionsResponse.php +++ b/Spanner/src/V1/ListSessionsResponse.php @@ -28,7 +28,7 @@ class ListSessionsResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string next_page_token = 2; */ - private $next_page_token = ''; + protected $next_page_token = ''; /** * Constructor. diff --git a/Spanner/src/V1/Mutation/Delete.php b/Spanner/src/V1/Mutation/Delete.php index 87497e39b097..205a4cfd6c7d 100644 --- a/Spanner/src/V1/Mutation/Delete.php +++ b/Spanner/src/V1/Mutation/Delete.php @@ -20,7 +20,7 @@ class Delete extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string table = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $table = ''; + protected $table = ''; /** * Required. The primary keys of the rows within [table][google.spanner.v1.Mutation.Delete.table] to delete. The * primary keys must be specified in the order in which they appear in the @@ -31,7 +31,7 @@ class Delete extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.KeySet key_set = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $key_set = null; + protected $key_set = null; /** * Constructor. @@ -129,6 +129,4 @@ public function setKeySet($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Delete::class, \Google\Cloud\Spanner\V1\Mutation_Delete::class); diff --git a/Spanner/src/V1/Mutation/Write.php b/Spanner/src/V1/Mutation/Write.php index a77397b49a31..74a612a2b61d 100644 --- a/Spanner/src/V1/Mutation/Write.php +++ b/Spanner/src/V1/Mutation/Write.php @@ -21,7 +21,7 @@ class Write extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string table = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $table = ''; + protected $table = ''; /** * The names of the columns in [table][google.spanner.v1.Mutation.Write.table] to be written. * The list of columns must contain enough columns to allow @@ -174,6 +174,4 @@ public function setValues($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Write::class, \Google\Cloud\Spanner\V1\Mutation_Write::class); diff --git a/Spanner/src/V1/Mutation_Delete.php b/Spanner/src/V1/Mutation_Delete.php deleted file mode 100644 index d02ffef898a6..000000000000 --- a/Spanner/src/V1/Mutation_Delete.php +++ /dev/null @@ -1,16 +0,0 @@ -bytes partition_token = 1; */ - private $partition_token = ''; + protected $partition_token = ''; /** * Constructor. diff --git a/Spanner/src/V1/PartitionOptions.php b/Spanner/src/V1/PartitionOptions.php index 89530cdb9f1e..fa726b5f9161 100644 --- a/Spanner/src/V1/PartitionOptions.php +++ b/Spanner/src/V1/PartitionOptions.php @@ -25,7 +25,7 @@ class PartitionOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int64 partition_size_bytes = 1; */ - private $partition_size_bytes = 0; + protected $partition_size_bytes = 0; /** * **Note:** This hint is currently ignored by PartitionQuery and * PartitionRead requests. @@ -37,7 +37,7 @@ class PartitionOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int64 max_partitions = 2; */ - private $max_partitions = 0; + protected $max_partitions = 0; /** * Constructor. diff --git a/Spanner/src/V1/PartitionQueryRequest.php b/Spanner/src/V1/PartitionQueryRequest.php index fe99ae2a25bb..98df014e6b07 100644 --- a/Spanner/src/V1/PartitionQueryRequest.php +++ b/Spanner/src/V1/PartitionQueryRequest.php @@ -20,14 +20,14 @@ class PartitionQueryRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Read only snapshot transactions are supported, read/write and single use * transactions are not. * * Generated from protobuf field .google.spanner.v1.TransactionSelector transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * Required. The query request to generate partitions for. The request will * fail if the query is not root partitionable. For a query to be root @@ -43,7 +43,7 @@ class PartitionQueryRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string sql = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $sql = ''; + protected $sql = ''; /** * Parameter names and values that bind to placeholders in the SQL string. * A parameter placeholder consists of the `@` character followed by the @@ -56,7 +56,7 @@ class PartitionQueryRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct params = 4; */ - private $params = null; + protected $params = null; /** * It is not always possible for Cloud Spanner to infer the right SQL type * from a JSON value. For example, values of type `BYTES` and values @@ -75,7 +75,7 @@ class PartitionQueryRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.PartitionOptions partition_options = 6; */ - private $partition_options = null; + protected $partition_options = null; /** * Constructor. diff --git a/Spanner/src/V1/PartitionReadRequest.php b/Spanner/src/V1/PartitionReadRequest.php index 64657d72bcd1..abf0b47074e2 100644 --- a/Spanner/src/V1/PartitionReadRequest.php +++ b/Spanner/src/V1/PartitionReadRequest.php @@ -20,20 +20,20 @@ class PartitionReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Read only snapshot transactions are supported, read/write and single use * transactions are not. * * Generated from protobuf field .google.spanner.v1.TransactionSelector transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * Required. The name of the table in the database to be read. * * Generated from protobuf field string table = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $table = ''; + protected $table = ''; /** * If non-empty, the name of an index on * [table][google.spanner.v1.PartitionReadRequest.table]. This index is used @@ -44,7 +44,7 @@ class PartitionReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string index = 4; */ - private $index = ''; + protected $index = ''; /** * The columns of [table][google.spanner.v1.PartitionReadRequest.table] to be * returned for each row matching this request. @@ -65,13 +65,13 @@ class PartitionReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.KeySet key_set = 6 [(.google.api.field_behavior) = REQUIRED]; */ - private $key_set = null; + protected $key_set = null; /** * Additional options that affect how many partitions are created. * * Generated from protobuf field .google.spanner.v1.PartitionOptions partition_options = 9; */ - private $partition_options = null; + protected $partition_options = null; /** * Constructor. diff --git a/Spanner/src/V1/PartitionResponse.php b/Spanner/src/V1/PartitionResponse.php index e60b8bbae38c..f73e1179d2df 100644 --- a/Spanner/src/V1/PartitionResponse.php +++ b/Spanner/src/V1/PartitionResponse.php @@ -27,7 +27,7 @@ class PartitionResponse extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.Transaction transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * Constructor. diff --git a/Spanner/src/V1/PlanNode.php b/Spanner/src/V1/PlanNode.php index e19b7fc47938..9137384574bb 100644 --- a/Spanner/src/V1/PlanNode.php +++ b/Spanner/src/V1/PlanNode.php @@ -20,7 +20,7 @@ class PlanNode extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 index = 1; */ - private $index = 0; + protected $index = 0; /** * Used to determine the type of node. May be needed for visualizing * different kinds of nodes differently. For example, If the node is a @@ -30,13 +30,13 @@ class PlanNode extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.PlanNode.Kind kind = 2; */ - private $kind = 0; + protected $kind = 0; /** * The display name for the node. * * Generated from protobuf field string display_name = 3; */ - private $display_name = ''; + protected $display_name = ''; /** * List of child node `index`es and their relationship to this parent. * @@ -48,7 +48,7 @@ class PlanNode extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.PlanNode.ShortRepresentation short_representation = 5; */ - private $short_representation = null; + protected $short_representation = null; /** * Attributes relevant to the node contained in a group of key-value pairs. * For example, a Parameter Reference node could have the following @@ -60,7 +60,7 @@ class PlanNode extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct metadata = 6; */ - private $metadata = null; + protected $metadata = null; /** * The execution statistics associated with the node, contained in a group of * key-value pairs. Only present if the plan was returned as a result of a @@ -69,7 +69,7 @@ class PlanNode extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct execution_stats = 7; */ - private $execution_stats = null; + protected $execution_stats = null; /** * Constructor. diff --git a/Spanner/src/V1/PlanNode/ChildLink.php b/Spanner/src/V1/PlanNode/ChildLink.php index 5da7b08010f6..ada6b260cb01 100644 --- a/Spanner/src/V1/PlanNode/ChildLink.php +++ b/Spanner/src/V1/PlanNode/ChildLink.php @@ -21,7 +21,7 @@ class ChildLink extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int32 child_index = 1; */ - private $child_index = 0; + protected $child_index = 0; /** * The type of the link. For example, in Hash Joins this could be used to * distinguish between the build child and the probe child, or in the case @@ -30,7 +30,7 @@ class ChildLink extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string type = 2; */ - private $type = ''; + protected $type = ''; /** * Only present if the child node is [SCALAR][google.spanner.v1.PlanNode.Kind.SCALAR] and corresponds * to an output variable of the parent node. The field carries the name of @@ -43,7 +43,7 @@ class ChildLink extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string variable = 3; */ - private $variable = ''; + protected $variable = ''; /** * Constructor. @@ -174,6 +174,4 @@ public function setVariable($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(ChildLink::class, \Google\Cloud\Spanner\V1\PlanNode_ChildLink::class); diff --git a/Spanner/src/V1/PlanNode/Kind.php b/Spanner/src/V1/PlanNode/Kind.php index 78081c797e03..ce60b0eadc0d 100644 --- a/Spanner/src/V1/PlanNode/Kind.php +++ b/Spanner/src/V1/PlanNode/Kind.php @@ -65,6 +65,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Kind::class, \Google\Cloud\Spanner\V1\PlanNode_Kind::class); diff --git a/Spanner/src/V1/PlanNode/ShortRepresentation.php b/Spanner/src/V1/PlanNode/ShortRepresentation.php index 700c523a9c0f..30993ae031c7 100644 --- a/Spanner/src/V1/PlanNode/ShortRepresentation.php +++ b/Spanner/src/V1/PlanNode/ShortRepresentation.php @@ -21,7 +21,7 @@ class ShortRepresentation extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string description = 1; */ - private $description = ''; + protected $description = ''; /** * A mapping of (subquery variable name) -> (subquery node id) for cases * where the `description` string of this node references a `SCALAR` @@ -116,6 +116,4 @@ public function setSubqueries($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(ShortRepresentation::class, \Google\Cloud\Spanner\V1\PlanNode_ShortRepresentation::class); diff --git a/Spanner/src/V1/PlanNode_ChildLink.php b/Spanner/src/V1/PlanNode_ChildLink.php deleted file mode 100644 index 6654ad37e08e..000000000000 --- a/Spanner/src/V1/PlanNode_ChildLink.php +++ /dev/null @@ -1,16 +0,0 @@ -string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * The transaction to use. If none is provided, the default is a * temporary read-only transaction with strong concurrency. * * Generated from protobuf field .google.spanner.v1.TransactionSelector transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * Required. The name of the table in the database to be read. * * Generated from protobuf field string table = 3 [(.google.api.field_behavior) = REQUIRED]; */ - private $table = ''; + protected $table = ''; /** * If non-empty, the name of an index on * [table][google.spanner.v1.ReadRequest.table]. This index is used instead of @@ -45,7 +45,7 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string index = 4; */ - private $index = ''; + protected $index = ''; /** * Required. The columns of [table][google.spanner.v1.ReadRequest.table] to be * returned for each row matching this request. @@ -71,7 +71,7 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.KeySet key_set = 6 [(.google.api.field_behavior) = REQUIRED]; */ - private $key_set = null; + protected $key_set = null; /** * If greater than zero, only the first `limit` rows are yielded. If `limit` * is zero, the default is no limit. A limit cannot be specified if @@ -79,7 +79,7 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field int64 limit = 8; */ - private $limit = 0; + protected $limit = 0; /** * If this request is resuming a previously interrupted read, * `resume_token` should be copied from the last @@ -90,7 +90,7 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes resume_token = 9; */ - private $resume_token = ''; + protected $resume_token = ''; /** * If present, results will be restricted to the specified partition * previously created using PartitionRead(). There must be an exact @@ -99,19 +99,19 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bytes partition_token = 10; */ - private $partition_token = ''; + protected $partition_token = ''; /** * Common options for this request. * * Generated from protobuf field .google.spanner.v1.RequestOptions request_options = 11; */ - private $request_options = null; + protected $request_options = null; /** * Directed read options for this request. * * Generated from protobuf field .google.spanner.v1.DirectedReadOptions directed_read_options = 14; */ - private $directed_read_options = null; + protected $directed_read_options = null; /** * If this is for a partitioned read and this field is set to `true`, the * request is executed with Spanner Data Boost independent compute resources. @@ -120,7 +120,7 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool data_boost_enabled = 15; */ - private $data_boost_enabled = false; + protected $data_boost_enabled = false; /** * Optional. Order for the returned rows. * By default, Spanner will return result rows in primary key order except for @@ -131,14 +131,14 @@ class ReadRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.ReadRequest.OrderBy order_by = 16 [(.google.api.field_behavior) = OPTIONAL]; */ - private $order_by = 0; + protected $order_by = 0; /** * Optional. Lock Hint for the request, it can only be used with read-write * transactions. * * Generated from protobuf field .google.spanner.v1.ReadRequest.LockHint lock_hint = 17 [(.google.api.field_behavior) = OPTIONAL]; */ - private $lock_hint = 0; + protected $lock_hint = 0; /** * Constructor. diff --git a/Spanner/src/V1/ReadRequest/LockHint.php b/Spanner/src/V1/ReadRequest/LockHint.php index c0e38b196dbf..5e1b4d9d2b3f 100644 --- a/Spanner/src/V1/ReadRequest/LockHint.php +++ b/Spanner/src/V1/ReadRequest/LockHint.php @@ -90,6 +90,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(LockHint::class, \Google\Cloud\Spanner\V1\ReadRequest_LockHint::class); diff --git a/Spanner/src/V1/ReadRequest/OrderBy.php b/Spanner/src/V1/ReadRequest/OrderBy.php index 4a229a9738f2..062f5ec846e3 100644 --- a/Spanner/src/V1/ReadRequest/OrderBy.php +++ b/Spanner/src/V1/ReadRequest/OrderBy.php @@ -62,6 +62,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(OrderBy::class, \Google\Cloud\Spanner\V1\ReadRequest_OrderBy::class); diff --git a/Spanner/src/V1/RequestOptions.php b/Spanner/src/V1/RequestOptions.php index 0b8217f4b287..1b2cd281d004 100644 --- a/Spanner/src/V1/RequestOptions.php +++ b/Spanner/src/V1/RequestOptions.php @@ -20,7 +20,7 @@ class RequestOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.RequestOptions.Priority priority = 1; */ - private $priority = 0; + protected $priority = 0; /** * A per-request tag which can be applied to queries or reads, used for * statistics collection. @@ -35,7 +35,7 @@ class RequestOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string request_tag = 2; */ - private $request_tag = ''; + protected $request_tag = ''; /** * A tag used for statistics collection about this transaction. * Both request_tag and transaction_tag can be specified for a read or query @@ -51,7 +51,7 @@ class RequestOptions extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string transaction_tag = 3; */ - private $transaction_tag = ''; + protected $transaction_tag = ''; /** * Constructor. diff --git a/Spanner/src/V1/RequestOptions/Priority.php b/Spanner/src/V1/RequestOptions/Priority.php index b8d614f98da5..06147292d32c 100644 --- a/Spanner/src/V1/RequestOptions/Priority.php +++ b/Spanner/src/V1/RequestOptions/Priority.php @@ -79,6 +79,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Priority::class, \Google\Cloud\Spanner\V1\RequestOptions_Priority::class); diff --git a/Spanner/src/V1/RequestOptions_Priority.php b/Spanner/src/V1/RequestOptions_Priority.php deleted file mode 100644 index 0eb2ae34000b..000000000000 --- a/Spanner/src/V1/RequestOptions_Priority.php +++ /dev/null @@ -1,16 +0,0 @@ -.google.spanner.v1.StructType row_type = 1; */ - private $row_type = null; + protected $row_type = null; /** * If the read or SQL query began a transaction as a side-effect, the * information about the new transaction is yielded here. * * Generated from protobuf field .google.spanner.v1.Transaction transaction = 2; */ - private $transaction = null; + protected $transaction = null; /** * A SQL query can be parameterized. In PLAN mode, these parameters can be * undeclared. This indicates the field names and types for those undeclared @@ -47,7 +47,7 @@ class ResultSetMetadata extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.StructType undeclared_parameters = 3; */ - private $undeclared_parameters = null; + protected $undeclared_parameters = null; /** * Constructor. diff --git a/Spanner/src/V1/ResultSetStats.php b/Spanner/src/V1/ResultSetStats.php index 8668c1211e06..e39cc1952951 100644 --- a/Spanner/src/V1/ResultSetStats.php +++ b/Spanner/src/V1/ResultSetStats.php @@ -20,7 +20,7 @@ class ResultSetStats extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.QueryPlan query_plan = 1; */ - private $query_plan = null; + protected $query_plan = null; /** * Aggregated statistics from the execution of the query. Only present when * the query is profiled. For example, a query could return the statistics as @@ -33,7 +33,7 @@ class ResultSetStats extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Struct query_stats = 2; */ - private $query_stats = null; + protected $query_stats = null; protected $row_count; /** diff --git a/Spanner/src/V1/RollbackRequest.php b/Spanner/src/V1/RollbackRequest.php index 9f7a3a2d89fb..23129ad2e295 100644 --- a/Spanner/src/V1/RollbackRequest.php +++ b/Spanner/src/V1/RollbackRequest.php @@ -20,13 +20,13 @@ class RollbackRequest extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string session = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { */ - private $session = ''; + protected $session = ''; /** * Required. The transaction to roll back. * * Generated from protobuf field bytes transaction_id = 2 [(.google.api.field_behavior) = REQUIRED]; */ - private $transaction_id = ''; + protected $transaction_id = ''; /** * @param string $session Required. The session in which the transaction to roll back is running. Please see diff --git a/Spanner/src/V1/Session.php b/Spanner/src/V1/Session.php index 0a273127adae..772272bb851a 100644 --- a/Spanner/src/V1/Session.php +++ b/Spanner/src/V1/Session.php @@ -20,7 +20,7 @@ class Session extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $name = ''; + protected $name = ''; /** * The labels for the session. * * Label keys must be between 1 and 63 characters long and must conform to @@ -38,20 +38,20 @@ class Session extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.protobuf.Timestamp create_time = 3 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $create_time = null; + protected $create_time = null; /** * Output only. The approximate timestamp when the session is last used. It is * typically earlier than the actual last use time. * * Generated from protobuf field .google.protobuf.Timestamp approximate_last_use_time = 4 [(.google.api.field_behavior) = OUTPUT_ONLY]; */ - private $approximate_last_use_time = null; + protected $approximate_last_use_time = null; /** * The database role which created this session. * * Generated from protobuf field string creator_role = 5; */ - private $creator_role = ''; + protected $creator_role = ''; /** * Optional. If true, specifies a multiplexed session. A multiplexed session * may be used for multiple, concurrent read-only operations but can not be @@ -63,7 +63,7 @@ class Session extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool multiplexed = 6 [(.google.api.field_behavior) = OPTIONAL]; */ - private $multiplexed = false; + protected $multiplexed = false; /** * Constructor. diff --git a/Spanner/src/V1/SpannerGrpcClient.php b/Spanner/src/V1/SpannerGrpcClient.php deleted file mode 100644 index fed418d3734b..000000000000 --- a/Spanner/src/V1/SpannerGrpcClient.php +++ /dev/null @@ -1,373 +0,0 @@ -_simpleRequest('/google.spanner.v1.Spanner/CreateSession', - $argument, - ['\Google\Cloud\Spanner\V1\Session', 'decode'], - $metadata, $options); - } - - /** - * Creates multiple new sessions. - * - * This API can be used to initialize a session cache on the clients. - * See https://goo.gl/TgSFN2 for best practices on session cache management. - * @param \Google\Cloud\Spanner\V1\BatchCreateSessionsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function BatchCreateSessions(\Google\Cloud\Spanner\V1\BatchCreateSessionsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/BatchCreateSessions', - $argument, - ['\Google\Cloud\Spanner\V1\BatchCreateSessionsResponse', 'decode'], - $metadata, $options); - } - - /** - * Gets a session. Returns `NOT_FOUND` if the session does not exist. - * This is mainly useful for determining whether a session is still - * alive. - * @param \Google\Cloud\Spanner\V1\GetSessionRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function GetSession(\Google\Cloud\Spanner\V1\GetSessionRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/GetSession', - $argument, - ['\Google\Cloud\Spanner\V1\Session', 'decode'], - $metadata, $options); - } - - /** - * Lists all sessions in a given database. - * @param \Google\Cloud\Spanner\V1\ListSessionsRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ListSessions(\Google\Cloud\Spanner\V1\ListSessionsRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/ListSessions', - $argument, - ['\Google\Cloud\Spanner\V1\ListSessionsResponse', 'decode'], - $metadata, $options); - } - - /** - * Ends a session, releasing server resources associated with it. This will - * asynchronously trigger cancellation of any operations that are running with - * this session. - * @param \Google\Cloud\Spanner\V1\DeleteSessionRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function DeleteSession(\Google\Cloud\Spanner\V1\DeleteSessionRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/DeleteSession', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Executes an SQL statement, returning all results in a single reply. This - * method cannot be used to return a result set larger than 10 MiB; - * if the query yields more data than that, the query fails with - * a `FAILED_PRECONDITION` error. - * - * Operations inside read-write transactions might return `ABORTED`. If - * this occurs, the application should restart the transaction from - * the beginning. See [Transaction][google.spanner.v1.Transaction] for more details. - * - * Larger result sets can be fetched in streaming fashion by calling - * [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] instead. - * @param \Google\Cloud\Spanner\V1\ExecuteSqlRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ExecuteSql(\Google\Cloud\Spanner\V1\ExecuteSqlRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/ExecuteSql', - $argument, - ['\Google\Cloud\Spanner\V1\ResultSet', 'decode'], - $metadata, $options); - } - - /** - * Like [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], except returns the result - * set as a stream. Unlike [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], there - * is no limit on the size of the returned result set. However, no - * individual row in the result set can exceed 100 MiB, and no - * column value can exceed 10 MiB. - * @param \Google\Cloud\Spanner\V1\ExecuteSqlRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\ServerStreamingCall - */ - public function ExecuteStreamingSql(\Google\Cloud\Spanner\V1\ExecuteSqlRequest $argument, - $metadata = [], $options = []) { - return $this->_serverStreamRequest('/google.spanner.v1.Spanner/ExecuteStreamingSql', - $argument, - ['\Google\Cloud\Spanner\V1\PartialResultSet', 'decode'], - $metadata, $options); - } - - /** - * Executes a batch of SQL DML statements. This method allows many statements - * to be run with lower latency than submitting them sequentially with - * [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. - * - * Statements are executed in sequential order. A request can succeed even if - * a statement fails. The [ExecuteBatchDmlResponse.status][google.spanner.v1.ExecuteBatchDmlResponse.status] field in the - * response provides information about the statement that failed. Clients must - * inspect this field to determine whether an error occurred. - * - * Execution stops after the first failed statement; the remaining statements - * are not executed. - * @param \Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function ExecuteBatchDml(\Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/ExecuteBatchDml', - $argument, - ['\Google\Cloud\Spanner\V1\ExecuteBatchDmlResponse', 'decode'], - $metadata, $options); - } - - /** - * Reads rows from the database using key lookups and scans, as a - * simple key/value style alternative to - * [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. This method cannot be used to - * return a result set larger than 10 MiB; if the read matches more - * data than that, the read fails with a `FAILED_PRECONDITION` - * error. - * - * Reads inside read-write transactions might return `ABORTED`. If - * this occurs, the application should restart the transaction from - * the beginning. See [Transaction][google.spanner.v1.Transaction] for more details. - * - * Larger result sets can be yielded in streaming fashion by calling - * [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead. - * @param \Google\Cloud\Spanner\V1\ReadRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function Read(\Google\Cloud\Spanner\V1\ReadRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/Read', - $argument, - ['\Google\Cloud\Spanner\V1\ResultSet', 'decode'], - $metadata, $options); - } - - /** - * Like [Read][google.spanner.v1.Spanner.Read], except returns the result set as a - * stream. Unlike [Read][google.spanner.v1.Spanner.Read], there is no limit on the - * size of the returned result set. However, no individual row in - * the result set can exceed 100 MiB, and no column value can exceed - * 10 MiB. - * @param \Google\Cloud\Spanner\V1\ReadRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\ServerStreamingCall - */ - public function StreamingRead(\Google\Cloud\Spanner\V1\ReadRequest $argument, - $metadata = [], $options = []) { - return $this->_serverStreamRequest('/google.spanner.v1.Spanner/StreamingRead', - $argument, - ['\Google\Cloud\Spanner\V1\PartialResultSet', 'decode'], - $metadata, $options); - } - - /** - * Begins a new transaction. This step can often be skipped: - * [Read][google.spanner.v1.Spanner.Read], [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and - * [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a - * side-effect. - * @param \Google\Cloud\Spanner\V1\BeginTransactionRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function BeginTransaction(\Google\Cloud\Spanner\V1\BeginTransactionRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/BeginTransaction', - $argument, - ['\Google\Cloud\Spanner\V1\Transaction', 'decode'], - $metadata, $options); - } - - /** - * Commits a transaction. The request includes the mutations to be - * applied to rows in the database. - * - * `Commit` might return an `ABORTED` error. This can occur at any time; - * commonly, the cause is conflicts with concurrent - * transactions. However, it can also happen for a variety of other - * reasons. If `Commit` returns `ABORTED`, the caller should re-attempt - * the transaction from the beginning, re-using the same session. - * - * On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, - * for example, if the client job experiences a 1+ hour networking failure. - * At that point, Cloud Spanner has lost track of the transaction outcome and - * we recommend that you perform another read from the database to see the - * state of things as they are now. - * @param \Google\Cloud\Spanner\V1\CommitRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function Commit(\Google\Cloud\Spanner\V1\CommitRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/Commit', - $argument, - ['\Google\Cloud\Spanner\V1\CommitResponse', 'decode'], - $metadata, $options); - } - - /** - * Rolls back a transaction, releasing any locks it holds. It is a good - * idea to call this for any transaction that includes one or more - * [Read][google.spanner.v1.Spanner.Read] or [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] requests and - * ultimately decides not to commit. - * - * `Rollback` returns `OK` if it successfully aborts the transaction, the - * transaction was already aborted, or the transaction is not - * found. `Rollback` never returns `ABORTED`. - * @param \Google\Cloud\Spanner\V1\RollbackRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function Rollback(\Google\Cloud\Spanner\V1\RollbackRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/Rollback', - $argument, - ['\Google\Protobuf\GPBEmpty', 'decode'], - $metadata, $options); - } - - /** - * Creates a set of partition tokens that can be used to execute a query - * operation in parallel. Each of the returned partition tokens can be used - * by [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] to specify a subset - * of the query result to read. The same session and read-only transaction - * must be used by the PartitionQueryRequest used to create the - * partition tokens and the ExecuteSqlRequests that use the partition tokens. - * - * Partition tokens become invalid when the session used to create them - * is deleted, is idle for too long, begins a new transaction, or becomes too - * old. When any of these happen, it is not possible to resume the query, and - * the whole operation must be restarted from the beginning. - * @param \Google\Cloud\Spanner\V1\PartitionQueryRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function PartitionQuery(\Google\Cloud\Spanner\V1\PartitionQueryRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/PartitionQuery', - $argument, - ['\Google\Cloud\Spanner\V1\PartitionResponse', 'decode'], - $metadata, $options); - } - - /** - * Creates a set of partition tokens that can be used to execute a read - * operation in parallel. Each of the returned partition tokens can be used - * by [StreamingRead][google.spanner.v1.Spanner.StreamingRead] to specify a subset of the read - * result to read. The same session and read-only transaction must be used by - * the PartitionReadRequest used to create the partition tokens and the - * ReadRequests that use the partition tokens. There are no ordering - * guarantees on rows returned among the returned partition tokens, or even - * within each individual StreamingRead call issued with a partition_token. - * - * Partition tokens become invalid when the session used to create them - * is deleted, is idle for too long, begins a new transaction, or becomes too - * old. When any of these happen, it is not possible to resume the read, and - * the whole operation must be restarted from the beginning. - * @param \Google\Cloud\Spanner\V1\PartitionReadRequest $argument input argument - * @param array $metadata metadata - * @param array $options call options - * @return \Grpc\UnaryCall - */ - public function PartitionRead(\Google\Cloud\Spanner\V1\PartitionReadRequest $argument, - $metadata = [], $options = []) { - return $this->_simpleRequest('/google.spanner.v1.Spanner/PartitionRead', - $argument, - ['\Google\Cloud\Spanner\V1\PartitionResponse', 'decode'], - $metadata, $options); - } - -} diff --git a/Spanner/src/V1/StructType/Field.php b/Spanner/src/V1/StructType/Field.php index 1ba9900e6011..5302c7521086 100644 --- a/Spanner/src/V1/StructType/Field.php +++ b/Spanner/src/V1/StructType/Field.php @@ -26,13 +26,13 @@ class Field extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string name = 1; */ - private $name = ''; + protected $name = ''; /** * The type of the field. * * Generated from protobuf field .google.spanner.v1.Type type = 2; */ - private $type = null; + protected $type = null; /** * Constructor. @@ -133,6 +133,4 @@ public function setType($var) } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(Field::class, \Google\Cloud\Spanner\V1\StructType_Field::class); diff --git a/Spanner/src/V1/StructType_Field.php b/Spanner/src/V1/StructType_Field.php deleted file mode 100644 index 61e68ce4d6fb..000000000000 --- a/Spanner/src/V1/StructType_Field.php +++ /dev/null @@ -1,16 +0,0 @@ -bool exclude_txn_from_change_streams = 5; */ - private $exclude_txn_from_change_streams = false; + protected $exclude_txn_from_change_streams = false; protected $mode; /** diff --git a/Spanner/src/V1/TransactionOptions/PBReadOnly.php b/Spanner/src/V1/TransactionOptions/PBReadOnly.php index bf4ce66fba43..c6310726aed4 100644 --- a/Spanner/src/V1/TransactionOptions/PBReadOnly.php +++ b/Spanner/src/V1/TransactionOptions/PBReadOnly.php @@ -22,7 +22,7 @@ class PBReadOnly extends \Google\Protobuf\Internal\Message * * Generated from protobuf field bool return_read_timestamp = 6; */ - private $return_read_timestamp = false; + protected $return_read_timestamp = false; protected $timestamp_bound; /** @@ -349,7 +349,7 @@ public function getTimestampBound() } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(PBReadOnly::class, \Google\Cloud\Spanner\V1\TransactionOptions_ReadOnly::class); +// Adding a class alias for backwards compatibility with the "readonly" keyword. +class_alias(PBReadOnly::class, __NAMESPACE__ . '\ReadOnly'); diff --git a/Spanner/src/V1/TransactionOptions/PartitionedDml.php b/Spanner/src/V1/TransactionOptions/PartitionedDml.php index 06d0e4b25c62..524e582cb398 100644 --- a/Spanner/src/V1/TransactionOptions/PartitionedDml.php +++ b/Spanner/src/V1/TransactionOptions/PartitionedDml.php @@ -31,6 +31,4 @@ public function __construct($data = NULL) { } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(PartitionedDml::class, \Google\Cloud\Spanner\V1\TransactionOptions_PartitionedDml::class); diff --git a/Spanner/src/V1/TransactionOptions/ReadWrite/ReadLockMode.php b/Spanner/src/V1/TransactionOptions/ReadWrite/ReadLockMode.php index 75b8922f96cb..45bdbaa5fc10 100644 --- a/Spanner/src/V1/TransactionOptions/ReadWrite/ReadLockMode.php +++ b/Spanner/src/V1/TransactionOptions/ReadWrite/ReadLockMode.php @@ -65,6 +65,4 @@ public static function value($name) } } -// Adding a class alias for backwards compatibility with the previous class name. -class_alias(ReadLockMode::class, \Google\Cloud\Spanner\V1\TransactionOptions_ReadWrite_ReadLockMode::class); diff --git a/Spanner/src/V1/TransactionOptions_PartitionedDml.php b/Spanner/src/V1/TransactionOptions_PartitionedDml.php deleted file mode 100644 index 642aa426c8ff..000000000000 --- a/Spanner/src/V1/TransactionOptions_PartitionedDml.php +++ /dev/null @@ -1,16 +0,0 @@ -.google.spanner.v1.TypeCode code = 1 [(.google.api.field_behavior) = REQUIRED]; */ - private $code = 0; + protected $code = 0; /** * If [code][google.spanner.v1.Type.code] == * [ARRAY][google.spanner.v1.TypeCode.ARRAY], then `array_element_type` is the @@ -29,7 +29,7 @@ class Type extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.Type array_element_type = 2; */ - private $array_element_type = null; + protected $array_element_type = null; /** * If [code][google.spanner.v1.Type.code] == * [STRUCT][google.spanner.v1.TypeCode.STRUCT], then `struct_type` provides @@ -37,7 +37,7 @@ class Type extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.StructType struct_type = 3; */ - private $struct_type = null; + protected $struct_type = null; /** * The [TypeAnnotationCode][google.spanner.v1.TypeAnnotationCode] that * disambiguates SQL type that Spanner will use to represent values of this @@ -50,7 +50,7 @@ class Type extends \Google\Protobuf\Internal\Message * * Generated from protobuf field .google.spanner.v1.TypeAnnotationCode type_annotation = 4; */ - private $type_annotation = 0; + protected $type_annotation = 0; /** * If [code][google.spanner.v1.Type.code] == * [PROTO][google.spanner.v1.TypeCode.PROTO] or @@ -60,7 +60,7 @@ class Type extends \Google\Protobuf\Internal\Message * * Generated from protobuf field string proto_type_fqn = 5; */ - private $proto_type_fqn = ''; + protected $proto_type_fqn = ''; /** * Constructor. diff --git a/Spanner/tests/OperationRefreshTrait.php b/Spanner/tests/OperationRefreshTrait.php deleted file mode 100644 index 8707f30fb379..000000000000 --- a/Spanner/tests/OperationRefreshTrait.php +++ /dev/null @@ -1,41 +0,0 @@ -___setProperty('operation', new Operation($connection, $returnInt64AsObject)); - return $stub; - } -} diff --git a/Spanner/tests/ResultGeneratorTrait.php b/Spanner/tests/ResultGeneratorTrait.php index 6d7ccfc74be8..eeadd1ea3775 100644 --- a/Spanner/tests/ResultGeneratorTrait.php +++ b/Spanner/tests/ResultGeneratorTrait.php @@ -17,81 +17,68 @@ namespace Google\Cloud\Spanner\Tests; +use Google\ApiCore\ServerStream; +use Google\Cloud\Spanner\V1\PartialResultSet; +use Google\Cloud\Spanner\V1\ResultSetMetadata; +use Google\Cloud\Spanner\V1\ResultSetStats; +use Google\Cloud\Spanner\V1\StructType; +use Google\Cloud\Spanner\V1\StructType\Field; +use Google\Cloud\Spanner\V1\Transaction; +use Google\Cloud\Spanner\V1\Type; use Google\Cloud\Spanner\Database; +use Google\Protobuf\Value; +use Google\Cloud\Spanner\Tests\Unit\Fixtures; /** * Provide a Spanner Read/Query result */ trait ResultGeneratorTrait { - /** - * Yield a ResultSet response. - * - * @param bool $withStats If true, statistics will be included. - * **Defaults to** `false`. - * @param string|null $transaction If set, the value will be included as the - * transaction ID. **Defaults to** `null`. - * @return \Generator - */ - private function resultGenerator($withStats = false, $transaction = null) - { - return $this->yieldRows([ - [ - 'name' => 'ID', - 'type' => Database::TYPE_INT64, - 'value' => '10' - ] - ], $withStats, $transaction); - } - /** * Yield rows with user-specified data. * * @param array[] $rows A list of arrays containing `name`, `type` and `value` keys. - * @param bool $withStats If true, statistics will be included. + * @param ResultSetStats|null $stats If true, statistics will be included. * **Defaults to** `false`. - * @param string|null $transaction If set, the value will be included as the + * @param string|null $transactionId If set, the value will be included as the * transaction ID. **Defaults to** `null`. * @return \Generator */ - private function yieldRows(array $rows, $withStats = false, $transaction = null) + private function yieldRows(array $rows, $stats = null, $transactionId = null) { $fields = []; $values = []; foreach ($rows as $row) { - $fields[] = [ + $fields[] = new Field([ 'name' => $row['name'], - 'type' => [ - 'code' => $row['type'] - ] - ]; + 'type' => new Type(['code' => $row['type']]) + ]); - $values[] = $row['value']; + $values[] = new Value(['string_value' => $row['value']]); } $result = [ - 'metadata' => [ - 'rowType' => [ + 'metadata' => new ResultSetMetadata([ + 'row_type' => new StructType([ 'fields' => $fields - ] - ], + ]) + ]), 'values' => $values ]; - if ($withStats) { - $result['stats'] = [ - 'rowCountExact' => 1, - 'rowCountLowerBound' => 1 - ]; + if ($stats) { + $result['stats'] = $stats; } - if ($transaction) { - $result['metadata']['transaction'] = [ - 'id' => $transaction - ]; + if ($transactionId) { + $result['metadata']->setTransaction(new Transaction(['id' => $transactionId])); } - yield $result; + if (isset($result['stats'])) { + $result['stats'] = $stats; + } + + yield new PartialResultSet($result); } /** @@ -104,4 +91,72 @@ private function resultGeneratorData(array $data) { yield $data; } + + private function resultGeneratorStream( + array $chunks = null, + ResultSetStats $stats = null, + string $transactionId = null + ) { + $this->stream = $this->prophesize(ServerStream::class); + if ($chunks) { + foreach ($chunks as $i => $chunk) { + $result = new PartialResultSet(); + $result->mergeFromJsonString($chunk); + $chunks[$i] = $result; + } + + $this->stream->readAll() + ->willReturn($this->resultGeneratorChunks($chunks)); + + } else { + $rows = [ + [ + 'name' => 'ID', + 'type' => Database::TYPE_INT64, + 'value' => '10' + ] + ]; + $this->stream->readAll() + ->willReturn($this->yieldRows($rows, $stats, $transactionId)); + } + + return $this->stream->reveal(); + } + + private function resultGeneratorChunks($chunks) + { + foreach ($chunks as $chunk) { + yield $chunk; + } + } + + private function resultGeneratorJson($chunks) + { + foreach ($chunks as $chunk) { + yield json_decode($chunk, true); + } + } + + private function getStreamingDataFixture() + { + return json_decode( + file_get_contents(Fixtures::STREAMING_READ_ACCEPTANCE_FIXTURE()), + true + ); + } + + public function streamingDataProviderFirstChunk() + { + foreach ($this->getStreamingDataFixture()['tests'] as $test) { + yield [$test['chunks'], $test['result']['value']]; + break; + } + } + + public function streamingDataProvider() + { + foreach ($this->getStreamingDataFixture()['tests'] as $test) { + yield [$test['chunks'], $test['result']['value']]; + } + } } diff --git a/Spanner/tests/Snippet/ArrayTypeTest.php b/Spanner/tests/Snippet/ArrayTypeTest.php index db0b98de2eac..b576c9cd979d 100644 --- a/Spanner/tests/Snippet/ArrayTypeTest.php +++ b/Spanner/tests/Snippet/ArrayTypeTest.php @@ -17,19 +17,18 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; use Google\Cloud\Spanner\ArrayType; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructType; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -40,15 +39,12 @@ class ArrayTypeTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; const INSTANCE = 'my-instance'; - private $connection; private $database; private $type; @@ -76,16 +72,16 @@ public function setUp(): void $sessionPool->setDatabase(Argument::any()) ->willReturn(null); - $this->connection = $this->getConnStub(); - $this->database = TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $this->serializer = new Serializer(); + $this->database = new Database( + $this->prophesize(SpannerClient::class)->reveal(), + $this->prophesize(DatabaseAdminClient::class)->reveal(), + $this->serializer, $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE, $sessionPool->reveal() - ], ['operation']); + ); } public function testConstructor() @@ -101,29 +97,37 @@ public function testConstructor() 'foo', 'bar', null ]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', 'SELECT @arrayParam as arrayValue'), - Argument::withEntry('params', [ - 'arrayParam' => $values - ]), - Argument::withEntry('paramTypes', [ - 'arrayParam' => $field - ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'arrayValue', - 'type' => $field + $this->spannerClient->executeStreamingSql( + Argument::that(function ($args) use ($values, $field) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals($message['sql'], 'SELECT @arrayParam as arrayValue'); + $this->assertEquals($message['params'], ['arrayParam' => $values]); + $this->assertEquals( + $message['paramTypes']['arrayParam']['arrayElementType']['code'], + $field['arrayElementType']['code'] + ); + $this->assertEquals( + $message['paramTypes']['arrayParam']['code'], + $field['code'] + ); + return true; + }), + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'arrayValue', + 'type' => $field + ] ] ] - ] - ], - 'values' => [$values] - ])); + ], + 'values' => [$values] + ]) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromClass(ArrayType::class); $snippet->replace('$database = $spanner->connect(\'my-instance\', \'my-database\');', ''); diff --git a/Spanner/tests/Snippet/BackupTest.php b/Spanner/tests/Snippet/BackupTest.php index ea4b68179451..fbd649ff0e0e 100644 --- a/Spanner/tests/Snippet/BackupTest.php +++ b/Spanner/tests/Snippet/BackupTest.php @@ -17,14 +17,13 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\LongRunning\Client\OperationsClient; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\SpannerClient; @@ -45,7 +44,8 @@ class BackupTest extends SnippetTestCase const DATABASE = 'my-database'; const BACKUP = 'my-backup'; - private $connection; + private $spannerClient; + private $serializer; private $backup; private $client; private $instance; @@ -55,25 +55,26 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->prophesize(ConnectionInterface::class); - $this->client = TestHelpers::stub(SpannerClient::class); + $this->serializer = new Serializer(); + $this->client = new SpannerClient( + [['projectId' => 'my-project']], + ['requestHandler', 'serializer'] + ); $this->expireTime = new \DateTime("+ 7 hours"); - $this->instance = TestHelpers::stub(Instance::class, [ - $this->connection->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], + $this->instance = new Instance( + $this->requestHandler->reveal(), + $this->serializer, self::PROJECT, self::INSTANCE - ], ['connection', 'lroConnection']); + ); - $this->backup = TestHelpers::stub(Backup::class, [ - $this->connection->reveal(), + $this->backup = new Backup( + $this->requestHandler->reveal(), + $this->serializer, $this->instance, - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, - self::BACKUP, - ], ['instance', 'connection', 'lroConnection']); + self::BACKUP + ); } public function testClass() @@ -93,16 +94,18 @@ public function testCreate() $snippet = $this->snippetFromMethod(Backup::class, 'create'); $snippet->addLocal('backup', $this->backup); - $this->connection->createBackup(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'createBackup', + null, + $this->getOperationResponseMock() + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testCreateCopy() @@ -110,16 +113,18 @@ public function testCreateCopy() $snippet = $this->snippetFromMethod(Backup::class, 'createCopy'); $snippet->addLocal('spanner', $this->client); - $this->connection->copyBackup(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'copyBackup', + null, + $this->getOperationResponseMock() + ); - $this->client->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testDelete() @@ -127,10 +132,15 @@ public function testDelete() $snippet = $this->snippetFromMethod(Backup::class, 'delete'); $snippet->addLocal('backup', $this->backup); - $this->connection->deleteBackup(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'deleteBackup', + null, + null + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -140,11 +150,15 @@ public function testExists() $snippet = $this->snippetFromMethod(Backup::class, 'exists'); $snippet->addLocal('backup', $this->backup); - $this->connection->getBackup(Argument::any()) - ->shouldBeCalled() - ->willReturn(['foo' => 'bar']); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getBackup', + null, + ['foo' => 'bar'] + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Backup exists!', $res->output()); @@ -157,11 +171,15 @@ public function testInfo() $snippet = $this->snippetFromMethod(Backup::class, 'info'); $snippet->addLocal('backup', $this->backup); - $this->connection->getBackup(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn($backup); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getBackup', + null, + $backup + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($backup, $res->returnVal()); @@ -187,11 +205,16 @@ public function testReload() $snippet = $this->snippetFromMethod(Backup::class, 'reload'); $snippet->addLocal('backup', $this->backup); - $this->connection->getBackup(Argument::any()) - ->shouldBeCalledTimes(2) - ->willReturn($bkp); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getBackup', + null, + $bkp, + 2 + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($bkp, $res->returnVal()); @@ -203,11 +226,15 @@ public function testState() $snippet = $this->snippetFromMethod(Backup::class, 'state'); $snippet->addLocal('backup', $this->backup); - $this->connection->getBackup(Argument::any()) - ->shouldBeCalledTimes(1) - ->WillReturn(['state' => Backup::STATE_READY]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getBackup', + null, + ['state' => Backup::STATE_READY] + ); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Backup is ready!', $res->output()); @@ -220,11 +247,16 @@ public function testUpdateExpireTime() $snippet = $this->snippetFromMethod(Backup::class, 'updateExpireTime'); $snippet->addLocal('backup', $this->backup); - $this->connection->updateBackup(Argument::any()) - ->shouldBeCalled() - ->willReturn($bkp); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'updateBackup', + null, + $bkp + ); + + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); - $this->backup->___setProperty('connection', $this->connection->reveal()); $res = $snippet->invoke('info'); $this->assertEquals($bkp, $res->returnVal()); @@ -237,7 +269,7 @@ public function testResumeOperation() $snippet->addLocal('operationName', 'foo'); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); $this->assertEquals('foo', $res->returnVal()->name()); } @@ -246,21 +278,22 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Backup::class, 'longRunningOperations'); $snippet->addLocal('backup', $this->backup); - $lroConnection = $this->prophesize(LongRunningConnectionInterface::class); - $lroConnection->operations(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'operations' => [ - [ - 'name' => 'foo' - ] - ] - ]); + $this->requestHandler + ->getClientObject(Argument::any()) + ->willReturn(new DatabaseAdminClient()); + $this->requestHandler + ->sendRequest( + Argument::any(), + 'listOperations', + Argument::cetera() + ) + ->willReturn([$this->getOperationResponseMock()]); - $this->backup->___setProperty('lroConnection', $lroConnection->reveal()); + $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); - $this->assertContainsOnlyInstancesOf(LongRunningOperation::class, $res->returnVal()); + $this->assertContainsOnlyInstancesOf(OperationResponse::class, $res->returnVal()); } } diff --git a/Spanner/tests/Snippet/Batch/BatchClientTest.php b/Spanner/tests/Snippet/Batch/BatchClientTest.php index f4a3e9319f2b..d385efca6046 100644 --- a/Spanner/tests/Snippet/Batch/BatchClientTest.php +++ b/Spanner/tests/Snippet/Batch/BatchClientTest.php @@ -17,6 +17,7 @@ namespace Google\Cloud\Spanner\Tests\Snippet\Batch; +use Google\ApiCore\Serializer; use Google\Cloud\Core\RequestHandler; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; @@ -27,12 +28,10 @@ use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\QueryPartition; -use Google\Cloud\Spanner\Connection\ConnectionInterface; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Operation; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; /** @@ -42,25 +41,24 @@ class BatchClientTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; - use StubCreationTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; - private $connection; + private $spannerClient; + private $serializer; private $client; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); - $this->client = TestHelpers::stub(BatchClient::class, [ - new Operation($this->connection->reveal(), false), + $this->serializer = new Serializer(); + $this->client = new BatchClient( + new Operation($this->requestHandler->reveal(), $this->serializer, false), self::DATABASE - ], ['operation']); + ); } public function testClass() @@ -141,50 +139,59 @@ public function testPubSubExample() $pubsub->___setProperty('requestHandler', $requestHandler->reveal()); // setup spanner service call stubs - $this->connection->partitionQuery(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->partitionQuery( + null, + [ 'partitions' => [ ['partitionToken' => $partition1->token()], ['partitionToken' => $partition2->token()] ] - ]); - - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('partitionToken', $partition1->token()), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('session', self::SESSION) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'loginCount', - 'type' => [ - 'code' => Database::TYPE_INT64 + ] + ); + + $this->spannerClient->executeStreamingSql( + function ($args) use ($partition1) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals( + $message['partitionToken'], + $partition1->token() + ); + $this->assertEquals( + $message['transaction']['id'], + self::TRANSACTION + ); + $this->assertEquals($message['session'], self::SESSION); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'loginCount', + 'type' => [ + 'code' => Database::TYPE_INT64 + ] ] ] ] - ] - ], - 'values' => [0] - ])); - - $this->connection->createSession(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => self::SESSION - ]); - - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + ], + 'values' => [0] + ]) + ); + $this->spannerClient->createSession( + null, + ['name' => self::SESSION] + ); + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, 'readTimestamp' => \DateTime::createFromFormat('U', (string) $time)->format(Timestamp::FORMAT) - ]); + ] + ); - $this->connection->deleteSession(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest(SpannerClient::class, 'deleteSession', null, null); // inject clients $publisher->addLocal('batch', $this->client); @@ -196,7 +203,7 @@ public function testPubSubExample() $subscriber->replace('$pubsub = new PubSubClient();', ''); $publisher->insertAfterLine(0, 'function processResult($res) {iterator_to_array($res);}'); - $this->refreshOperation($this->client, $this->connection->reveal()); + $this->refreshOperation($this->client, $this->requestHandler->reveal(), $this->serializer); $publisher->invoke(); $subscriber->invoke(); @@ -209,19 +216,20 @@ public function testSnapshot() $time = time(); - $this->connection->createSession(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn([ - 'name' => self::SESSION - ]); - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, 'readTimestamp' => \DateTime::createFromFormat('U', (string) $time)->format(Timestamp::FORMAT) - ]); - - $this->refreshOperation($this->client, $this->connection->reveal()); + ] + ); + $this->spannerClient->createSession( + null, + [ + 'name' => self::SESSION + ] + ); + $this->refreshOperation($this->client, $this->requestHandler->reveal(), $this->serializer); $res = $snippet->invoke('snapshot'); $this->assertInstanceOf(BatchSnapshot::class, $res->returnVal()); diff --git a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php index d35ac96696a3..a50895d1540e 100644 --- a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php @@ -28,10 +28,8 @@ use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\V1\SpannerClient; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -42,15 +40,14 @@ class BatchSnapshotTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; - private $connection; + private $spannerClient; + private $serializer; private $session; private $time; private $snapshot; @@ -59,7 +56,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $sessData = SpannerClient::parseName(self::SESSION, 'session'); $this->session = $this->prophesize(Session::class); @@ -71,7 +68,7 @@ public function setUp(): void $this->time = time(); $this->snapshot = TestHelpers::stub(BatchSnapshot::class, [ - new Operation($this->connection->reveal(), false), + new Operation($this->requestHandler->reveal(), $this->serializer, false), $this->session->reveal(), [ 'id' => self::TRANSACTION, @@ -82,20 +79,23 @@ public function setUp(): void public function testClass() { - $this->connection->createSession(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn([ - 'name' => self::SESSION - ]); - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->createSession( + null, + ['name' => self::SESSION] + ); + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, - 'readTimestamp' => \DateTime::createFromFormat('U', (string) $this->time)->format(Timestamp::FORMAT) - ]); + 'readTimestamp' => \DateTime::createFromFormat( + 'U', + (string) $this->time + )->format(Timestamp::FORMAT) + ] + ); $client = TestHelpers::stub(BatchClient::class, [ - new Operation($this->connection->reveal(), false), + new Operation($this->requestHandler->reveal(), $this->serializer, false), self::DATABASE ]); @@ -142,15 +142,17 @@ public function testClose() */ public function testPartitionRead($method) { - $this->connection->$method(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->mockSendRequest( + SpannerClient::class, + $method, + null, + [ 'partitions' => [ ['partitionToken' => 'foo'] ] - ]); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + ] + ); + $this->refreshOperation($this->snapshot, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(BatchSnapshot::class, $method); $snippet->addLocal('snapshot', $this->snapshot); @@ -171,9 +173,9 @@ public function testExecutePartition() $opts = []; $partition = new QueryPartition($token, $sql, $opts); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator([ + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator([ 'metadata' => [ 'rowType' => [ 'fields' => [ @@ -187,9 +189,9 @@ public function testExecutePartition() ] ], 'values' => [0] - ])); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + ]) + ); + $this->refreshOperation($this->snapshot, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(BatchSnapshot::class, 'executePartition'); $snippet->addLocal('snapshot', $this->snapshot); diff --git a/Spanner/tests/Snippet/Batch/QueryPartitionTest.php b/Spanner/tests/Snippet/Batch/QueryPartitionTest.php index 37c89a92aac9..7a4e5528e666 100644 --- a/Spanner/tests/Snippet/Batch/QueryPartitionTest.php +++ b/Spanner/tests/Snippet/Batch/QueryPartitionTest.php @@ -23,8 +23,8 @@ use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Operation; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; /** @@ -35,12 +35,13 @@ class QueryPartitionTest extends SnippetTestCase { use GrpcTestTrait; use PartitionSharedSnippetTestTrait; - use StubCreationTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; + private $spannerClient; + private $serializer; private $className = QueryPartition::class; private $sql = 'SELECT 1=1'; private $time; @@ -49,34 +50,35 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); + $this->serializer = new Serializer(); $this->time = time(); $this->partition = new QueryPartition($this->token, $this->sql, $this->options); } public function testClass() { - $connection = $this->getConnStub(); - $connection->createSession(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn([ - 'name' => self::SESSION - ]); - $connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->createSession( + null, + ['name' => self::SESSION] + ); + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, 'readTimestamp' => \DateTime::createFromFormat('U', (string) $this->time)->format(Timestamp::FORMAT) - ]); - $connection->partitionQuery(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + ] + ); + $this->spannerClient->partitionQuery( + null, + [ 'partitions' => [ ['partitionToken' => 'foo'] ] - ]); + ] + ); $client = TestHelpers::stub(BatchClient::class, [ - new Operation($connection->reveal(), false), + new Operation($this->requestHandler->reveal(), $this->serializer, false), self::DATABASE ]); diff --git a/Spanner/tests/Snippet/Batch/ReadPartitionTest.php b/Spanner/tests/Snippet/Batch/ReadPartitionTest.php index c32e3af55516..8cc0efa2de6b 100644 --- a/Spanner/tests/Snippet/Batch/ReadPartitionTest.php +++ b/Spanner/tests/Snippet/Batch/ReadPartitionTest.php @@ -24,8 +24,8 @@ use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; /** @@ -38,12 +38,13 @@ class ReadPartitionTest extends SnippetTestCase use PartitionSharedSnippetTestTrait { provideGetters as private getters; } - use StubCreationTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; + private $spannerClient; + private $serializer; private $className = ReadPartition::class; private $time; private $table; @@ -54,6 +55,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); + $this->serializer = new Serializer(); $this->time = time(); $this->table = 'table'; $this->keySet = new KeySet(['all' => true]); @@ -63,29 +65,31 @@ public function setUp(): void public function testClass() { - $connection = $this->getConnStub(); - $connection->createSession(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn([ - 'name' => self::SESSION - ]); - $connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->createSession( + null, + ['name' => self::SESSION] + ); + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, 'readTimestamp' => \DateTime::createFromFormat('U', (string) $this->time)->format(Timestamp::FORMAT) - ]); - $connection->partitionRead(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + ] + ); + $this->spannerClient->partitionRead( + null, + [ 'partitions' => [ ['partitionToken' => 'foo'] ] - ]); + ] + ); $client = TestHelpers::stub(BatchClient::class, [ - new Operation($connection->reveal(), false), + new Operation($this->requestHandler->reveal(), $this->serializer, false), self::DATABASE + ], [ + 'operation' ]); $snippet = $this->snippetFromClass(ReadPartition::class); diff --git a/Spanner/tests/Snippet/BatchDmlResultTest.php b/Spanner/tests/Snippet/BatchDmlResultTest.php index f6945b8ca479..0a1f7e1e10fe 100644 --- a/Spanner/tests/Snippet/BatchDmlResultTest.php +++ b/Spanner/tests/Snippet/BatchDmlResultTest.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; @@ -27,7 +26,7 @@ use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -38,15 +37,17 @@ class BatchDmlResultTest extends SnippetTestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; use TimeTrait; + private $spannerClient; + private $serializer; private $result; public function setUp(): void { $this->checkAndSkipGrpcTests(); + $this->serializer = new Serializer(); $this->result = new BatchDmlResult([ 'resultSets' => [ [ @@ -69,24 +70,22 @@ public function setUp(): void public function testClass() { - $connection = $this->getConnStub(); - $connection->executeBatchDml(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'resultSets' => [] - ]); - - $connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'id' => 'ddfdfd' - ]); - - $connection->commit(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->executeBatchDml( + null, + ['resultSets' => []] + ); + + $this->spannerClient->beginTransaction( + null, + ['id' => 'id'] + ); + + $this->spannerClient->commit( + null, + [ 'commitTimestamp' => $this->formatTimeAsString(new \DateTime, 0) - ]); + ] + ); $session = $this->prophesize(Session::class); $session->name()->willReturn( @@ -109,10 +108,9 @@ public function testClass() $instance->directedReadOptions()->willReturn([]); $database = TestHelpers::stub(Database::class, [ - $connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], 'test-project', 'projects/test-project/instances/my-instance/databases/my-database', $sessionPool->reveal() diff --git a/Spanner/tests/Snippet/CommitTimestampTest.php b/Spanner/tests/Snippet/CommitTimestampTest.php index 1f5dc9ce7f5a..c8cd4abb0d68 100644 --- a/Spanner/tests/Snippet/CommitTimestampTest.php +++ b/Spanner/tests/Snippet/CommitTimestampTest.php @@ -23,7 +23,7 @@ use Google\Cloud\Core\Timestamp; use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\SpannerClient; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; use Prophecy\Argument; /** @@ -33,12 +33,15 @@ class CommitTimestampTest extends SnippetTestCase { use GrpcTestTrait; - use StubCreationTrait; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; + private $spannerClient; + private $serializer; + public function setUp(): void { + $this->serializer = new Serializer(); $this->checkAndSkipGrpcTests(); } @@ -46,31 +49,50 @@ public function testClass() { $id = 'abc'; - $client = TestHelpers::stub(SpannerClient::class); - $conn = $this->getConnStub(); - $conn->createSession(Argument::any()) - ->willReturn([ - 'name' => self::SESSION - ]); + $client = new SpannerClient( + [['projectId' => 'my-project']], + ['requestHandler', 'serializer'] + ); + $this->mockSendRequest( + GapicSpannerClient::class, + 'createSession', + null, + ['name' => self::SESSION] + ); + $this->mockSendRequest( + GapicSpannerClient::class, + 'deleteSession', + null, + null + ); $mutation = [ 'insert' => [ 'table' => 'myTable', 'columns' => ['id', 'commitTimestamp'], - 'values' => [$id, CommitTimestamp::SPECIAL_VALUE] + 'values' => [[$id, CommitTimestamp::SPECIAL_VALUE]] ] ]; + $this->mockSendRequest( + GapicSpannerClient::class, + 'commit', + function ($args) use ($mutation) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals($message['mutations'][0], $mutation); + return true; + }, + [ + 'commitTimestamp' => \DateTime::createFromFormat('U', (string) time())->format(Timestamp::FORMAT) + ] + ); - $conn->commit(Argument::withEntry('mutations', [$mutation]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => \DateTime::createFromFormat('U', (string) time())->format(Timestamp::FORMAT) - ]); - - $client->___setProperty('connection', $conn->reveal()); + $client->___setProperty('requestHandler', $this->requestHandler->reveal()); + $client->___setProperty('serializer', $this->serializer); $snippet = $this->snippetFromClass(CommitTimestamp::class); $snippet->addLocal('id', $id); $snippet->addLocal('spanner', $client); - $snippet->replace('$spanner = new SpannerClient();', ''); + $snippet->replace("\$spanner = new SpannerClient(['projectId' => 'my-project']);", ''); $snippet->invoke(); } diff --git a/Spanner/tests/Snippet/DatabaseTest.php b/Spanner/tests/Snippet/DatabaseTest.php index 486efd3922e0..29e80bd43d07 100644 --- a/Spanner/tests/Snippet/DatabaseTest.php +++ b/Spanner/tests/Snippet/DatabaseTest.php @@ -17,16 +17,14 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\Iam\Iam; +use Google\LongRunning\Client\OperationsClient; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; @@ -35,11 +33,10 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -50,10 +47,8 @@ class DatabaseTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; @@ -61,7 +56,8 @@ class DatabaseTest extends SnippetTestCase const TRANSACTION = 'my-transaction'; const BACKUP = 'my-backup'; - private $connection; + private $spannerClient; + private $serializer; private $database; private $instance; @@ -86,24 +82,21 @@ public function setUp(): void ->willReturn(null); $sessionPool->clear()->willReturn(null); - $this->connection = $this->prophesize(ConnectionInterface::class); - $this->instance = TestHelpers::stub(Instance::class, [ - $this->connection->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], + $this->serializer = new Serializer(); + $this->instance = new Instance( + $this->requestHandler->reveal(), + $this->serializer, self::PROJECT, - self::INSTANCE - ], ['connection', 'lroConnection']); + self::INSTANCE ); $this->database = TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, $this->instance, - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE, $sessionPool->reveal() - ], ['connection', 'operation', 'lroConnection']); + ], ['requestHandler', 'serializer', 'operation']); } public function testClass() @@ -135,11 +128,15 @@ public function testState() $snippet->addLocal('database', $this->database); $snippet->addUse(Database::class); - $this->connection->getDatabase(Argument::any()) - ->shouldBeCalledTimes(1) - ->WillReturn(['state' => Database::STATE_READY]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getDatabase', + null, + ['state' => Database::STATE_READY] + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Database is ready!', $res->output()); @@ -153,19 +150,21 @@ public function testBackups() $snippet = $this->snippetFromMethod(Database::class, 'backups'); $snippet->addLocal('database', $this->database); - $this->connection->listBackups(Argument::any()) - ->shouldBeCalled() - ->WillReturn([ + $this->mockSendRequest( + DatabaseAdminClient::class, + 'listBackups', + null, + [ 'backups' => [ [ 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP) ] ] - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + ] + ); - $res = $snippet->invoke('backups'); + $this->instance->___setProperty('requestHandler', $this->requestHandler->reveal()); + $res = $snippet->invoke('backups'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertContainsOnlyInstancesOf(Backup::class, $res->returnVal()); @@ -179,13 +178,17 @@ public function testCreateBackup() $snippet = $this->snippetFromMethod(Database::class, 'createBackup'); $snippet->addLocal('database', $this->database); - $this->connection->createBackup(Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn(['name' => 'my-operations']); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'createBackup', + null, + $this->getOperationResponseMock() + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testName() @@ -204,11 +207,15 @@ public function testExists() $snippet = $this->snippetFromMethod(Database::class, 'exists'); $snippet->addLocal('database', $this->database); - $this->connection->getDatabase(Argument::any()) - ->shouldBeCalled() - ->willReturn(['statements' => []]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getDatabase', + null, + ['statements' => []] + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Database exists!', $res->output()); @@ -224,11 +231,15 @@ public function testInfo() $snippet = $this->snippetFromMethod(Database::class, 'info'); $snippet->addLocal('database', $this->database); - $this->connection->getDatabase(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn($db); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getDatabase', + null, + $db + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($db, $res->returnVal()); @@ -245,11 +256,16 @@ public function testReload() $snippet = $this->snippetFromMethod(Database::class, 'reload'); $snippet->addLocal('database', $this->database); - $this->connection->getDatabase(Argument::any()) - ->shouldBeCalledTimes(2) - ->willReturn($db); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getDatabase', + null, + $db, + 2 + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($db, $res->returnVal()); @@ -264,16 +280,18 @@ public function testCreate() $snippet = $this->snippetFromMethod(Database::class, 'create'); $snippet->addLocal('database', $this->database); - $this->connection->createDatabase(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'createDatabase', + null, + $this->getOperationResponseMock() + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } /** @@ -286,16 +304,18 @@ public function testRestore() $snippet->addLocal('database', $this->database); $snippet->addLocal('backup', $backup); - $this->connection->restoreDatabase(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'restoreDatabase', + null, + $this->getOperationResponseMock() + ); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } /** @@ -306,13 +326,15 @@ public function testUpdateDdl() $snippet = $this->snippetFromMethod(Database::class, 'updateDdl'); $snippet->addLocal('database', $this->database); - $this->connection->updateDatabaseDdl(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'updateDatabaseDdl', + null, + $this->getOperationResponseMock() + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -325,13 +347,15 @@ public function testUpdateDdlBatch() $snippet = $this->snippetFromMethod(Database::class, 'updateDdlBatch'); $snippet->addLocal('database', $this->database); - $this->connection->updateDatabaseDdl(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'updateDatabaseDdl', + null, + $this->getOperationResponseMock() + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -344,10 +368,15 @@ public function testDrop() $snippet = $this->snippetFromMethod(Database::class, 'drop'); $snippet->addLocal('database', $this->database); - $this->connection->dropDatabase(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'dropDatabase', + null, + null + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -365,13 +394,15 @@ public function testDdl() 'CREATE TABLE TestCases' ]; - $this->connection->getDatabaseDDL(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'statements' => $stmts - ]); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'getDatabaseDdl', + null, + ['statements' => $stmts] + ); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('statements'); $this->assertEquals($stmts, $res->returnVal()); @@ -379,13 +410,12 @@ public function testDdl() public function testSnapshot() { - $this->connection->beginTransaction(Argument::any(), Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->beginTransaction( + null, + ['id' => self::TRANSACTION] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'snapshot'); $snippet->addLocal('database', $this->database); @@ -396,14 +426,15 @@ public function testSnapshot() public function testSnapshotReadTimestamp() { - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->beginTransaction( + null, + [ 'id' => self::TRANSACTION, 'readTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'snapshot', 1); $snippet->addLocal('database', $this->database); @@ -414,32 +445,30 @@ public function testSnapshotReadTimestamp() public function testRunTransaction() { - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'id' => self::TRANSACTION - ]); - - $this->connection->commit(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->beginTransaction( + null, + ['id' => self::TRANSACTION] + ); - $this->connection->rollback(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->commit( + null, + ['commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString()] + ); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->yieldRows([ + $this->spannerClient->executeStreamingSql( + null, + $this->yieldRows([ [ 'name' => 'loginCount', 'type' => Database::TYPE_INT64, 'value' => 0 ] - ])); + ]) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); + + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'runTransaction'); $snippet->addUse(Transaction::class); @@ -452,20 +481,21 @@ public function testRunTransaction() public function testRunTransactionRollback() { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->commit(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->commit(Argument::cetera())->shouldNotBeCalled(); - $this->connection->rollback(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 1); - $this->connection->executeStreamingSql( - Argument::withEntry('transaction', ['begin' => ['readWrite' => []]]) - ) - ->shouldBeCalled() - ->willReturn($this->resultGeneratorData([ + $this->spannerClient->executeStreamingSql( + function ($args) { + $this->assertEquals( + $this->serializer->encodeMessage($args)['transaction']['begin']['readWrite'], + ['readLockMode' => 0] + ); + return true; + }, + $this->resultGeneratorData([ 'metadata' => [ 'rowType' => [ 'fields' => [ @@ -481,9 +511,10 @@ public function testRunTransactionRollback() 'id' => self::TRANSACTION ] ] - ])); + ]) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'runTransaction'); $snippet->addUse(Transaction::class); @@ -496,13 +527,12 @@ public function testRunTransactionRollback() public function testTransaction() { - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->beginTransaction( + null, + ['id' => self::TRANSACTION] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'transaction'); $snippet->addLocal('database', $this->database); @@ -512,13 +542,17 @@ public function testTransaction() public function testInsert() { - $this->connection->commit(Argument::that(function ($args) { - return isset($args['mutations'][0]['insert']); - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['insert']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'insert'); $snippet->addLocal('database', $this->database); @@ -528,21 +562,18 @@ public function testInsert() public function testInsertBatch() { - $this->connection->commit(Argument::that(function ($args) { - if (!isset($args['mutations'][0]['insert'])) { - return false; - } - - if (!isset($args['mutations'][1]['insert'])) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['insert']) + && isset($message['mutations'][1]['insert']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'insertBatch'); $snippet->addLocal('database', $this->database); @@ -551,13 +582,17 @@ public function testInsertBatch() public function testUpdate() { - $this->connection->commit(Argument::that(function ($args) { - return isset($args['mutations'][0]['update']); - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['update']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'update'); $snippet->addLocal('database', $this->database); @@ -567,21 +602,18 @@ public function testUpdate() public function testUpdateBatch() { - $this->connection->commit(Argument::that(function ($args) { - if (!isset($args['mutations'][0]['update'])) { - return false; - } - - if (!isset($args['mutations'][1]['update'])) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['update']) + && isset($message['mutations'][1]['update']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'updateBatch'); $snippet->addLocal('database', $this->database); @@ -590,13 +622,17 @@ public function testUpdateBatch() public function testInsertOrUpdate() { - $this->connection->commit(Argument::that(function ($args) { - return isset($args['mutations'][0]['insertOrUpdate']); - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['insertOrUpdate']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'insertOrUpdate'); $snippet->addLocal('database', $this->database); @@ -606,21 +642,18 @@ public function testInsertOrUpdate() public function testInsertOrUpdateBatch() { - $this->connection->commit(Argument::that(function ($args) { - if (!isset($args['mutations'][0]['insertOrUpdate'])) { - return false; - } - - if (!isset($args['mutations'][1]['insertOrUpdate'])) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['insertOrUpdate']) + && isset($message['mutations'][1]['insertOrUpdate']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'insertOrUpdateBatch'); $snippet->addLocal('database', $this->database); @@ -629,13 +662,17 @@ public function testInsertOrUpdateBatch() public function testReplace() { - $this->connection->commit(Argument::that(function ($args) { - return isset($args['mutations'][0]['replace']); - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['replace']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'replace'); $snippet->addLocal('database', $this->database); @@ -645,21 +682,18 @@ public function testReplace() public function testReplaceBatch() { - $this->connection->commit(Argument::that(function ($args) { - if (!isset($args['mutations'][0]['replace'])) { - return false; - } - - if (!isset($args['mutations'][1]['replace'])) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['replace']) + && isset($message['mutations'][1]['replace']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'replaceBatch'); $snippet->addLocal('database', $this->database); @@ -668,13 +702,17 @@ public function testReplaceBatch() public function testDelete() { - $this->connection->commit(Argument::that(function ($args) { - return isset($args['mutations'][0]['delete']); - }))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + $this->spannerClient->commit( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['mutations'][0]['delete']); + }, + [ + 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + ] + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'delete'); $snippet->addUse(KeySet::class); @@ -684,11 +722,12 @@ public function testDelete() public function testExecute() { - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator() + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'execute'); $snippet->addLocal('database', $this->database); @@ -699,37 +738,31 @@ public function testExecute() public function testExecuteWithParameterType() { - $this->connection->executeStreamingSql(Argument::that(function ($arg) { - if (!isset($arg['params'])) { - return false; - } - - if (!isset($arg['paramTypes'])) { - return false; - } - - if ($arg['paramTypes']['timestamp']['code'] !== Database::TYPE_TIMESTAMP) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn($this->resultGeneratorData([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'timestamp', - 'type' => [ - 'code' => Database::TYPE_TIMESTAMP + $this->spannerClient->executeStreamingSql( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['params']) + && isset($message['paramTypes']) + && $message['paramTypes']['timestamp']['code'] === Database::TYPE_TIMESTAMP; + }, + $this->resultGeneratorData([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'timestamp', + 'type' => [ + 'code' => Database::TYPE_TIMESTAMP + ] ] ] ] - ] - ], - 'values' => [null] - ])); + ], + 'values' => [null] + ]) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'execute', 1); $snippet->addLocal('database', $this->database); @@ -740,44 +773,36 @@ public function testExecuteWithParameterType() public function testExecuteWithEmptyArray() { - $this->connection->executeStreamingSql(Argument::that(function ($arg) { - if (!isset($arg['params'])) { - return false; - } - - if (!isset($arg['paramTypes'])) { - return false; - } - - if ($arg['paramTypes']['emptyArrayOfIntegers']['code'] !== Database::TYPE_ARRAY) { - return false; - } - - if ($arg['paramTypes']['emptyArrayOfIntegers']['arrayElementType']['code'] !== Database::TYPE_INT64) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn($this->resultGeneratorData([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'numbers', - 'type' => [ - 'code' => Database::TYPE_ARRAY, - 'arrayElementType' => [ - 'code' => Database::TYPE_INT64 + $this->spannerClient->executeStreamingSql( + function ($args) { + $message = $this->serializer->encodeMessage($args); + return isset($message['params']) + && isset($message['paramTypes']) + && $message['paramTypes']['emptyArrayOfIntegers']['code'] === Database::TYPE_ARRAY + && $message['paramTypes']['emptyArrayOfIntegers']['arrayElementType']['code'] + === Database::TYPE_INT64; + }, + $this->resultGeneratorData([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'numbers', + 'type' => [ + 'code' => Database::TYPE_ARRAY, + 'arrayElementType' => [ + 'code' => Database::TYPE_INT64 + ] ] ] ] ] - ] - ], - 'values' => [[]] - ])); + ], + 'values' => [[]] + ]) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'execute', 2); $snippet->addLocal('database', $this->database); @@ -788,11 +813,12 @@ public function testExecuteWithEmptyArray() public function testExecuteBeginSnapshot() { - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(false, self::TRANSACTION)); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator(false, self::TRANSACTION) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'execute', 5); $snippet->addLocal('database', $this->database); @@ -804,11 +830,12 @@ public function testExecuteBeginSnapshot() public function testExecuteBeginTransaction() { - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(false, self::TRANSACTION)); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator(false, self::TRANSACTION) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'execute', 6); $snippet->addLocal('database', $this->database); @@ -820,17 +847,17 @@ public function testExecuteBeginTransaction() public function testExecutePartitionedUpdate() { - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->beginTransaction( + null, + ['id' => self::TRANSACTION] + ); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(true)); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator(true) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'executePartitionedUpdate'); $snippet->addLocal('database', $this->database); @@ -841,11 +868,12 @@ public function testExecutePartitionedUpdate() public function testRead() { - $this->connection->streamingRead(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->streamingRead( + null, + $this->resultGenerator() + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'read'); $snippet->addLocal('database', $this->database); @@ -856,11 +884,12 @@ public function testRead() public function testReadWithSnapshot() { - $this->connection->streamingRead(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(false, self::TRANSACTION)); + $this->spannerClient->streamingRead( + null, + $this->resultGenerator(false, self::TRANSACTION) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'read', 1); $snippet->addLocal('database', $this->database); @@ -872,11 +901,12 @@ public function testReadWithSnapshot() public function testReadWithTransaction() { - $this->connection->streamingRead(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(false, self::TRANSACTION)); + $this->spannerClient->streamingRead( + null, + $this->resultGenerator(false, self::TRANSACTION) + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Database::class, 'read', 2); $snippet->addLocal('database', $this->database); @@ -909,7 +939,7 @@ public function testIam() $snippet->addLocal('database', $this->database); $res = $snippet->invoke('iam'); - $this->assertInstanceOf(Iam::class, $res->returnVal()); + $this->assertInstanceOf(IamManager::class, $res->returnVal()); } public function testResumeOperation() @@ -919,7 +949,7 @@ public function testResumeOperation() $snippet->addLocal('operationName', 'foo'); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); $this->assertEquals('foo', $res->returnVal()->name()); } @@ -928,21 +958,22 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Database::class, 'longRunningOperations'); $snippet->addLocal('database', $this->database); - $lroConnection = $this->prophesize(LongRunningConnectionInterface::class); - $lroConnection->operations(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'operations' => [ - [ - 'name' => 'foo' - ] - ] - ]); + $this->requestHandler + ->getClientObject(Argument::any()) + ->willReturn(new DatabaseAdminClient()); + $this->requestHandler + ->sendRequest( + Argument::any(), + 'listOperations', + Argument::cetera() + ) + ->willReturn([$this->getOperationResponseMock()]); - $this->database->___setProperty('lroConnection', $lroConnection->reveal()); + $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); - $this->assertContainsOnlyInstancesOf(LongRunningOperation::class, $res->returnVal()); + $this->assertContainsOnlyInstancesOf(OperationResponse::class, $res->returnVal()); } } diff --git a/Spanner/tests/Snippet/DurationTest.php b/Spanner/tests/Snippet/DurationTest.php deleted file mode 100644 index 58b0fab6a1d4..000000000000 --- a/Spanner/tests/Snippet/DurationTest.php +++ /dev/null @@ -1,85 +0,0 @@ -checkAndSkipGrpcTests(); - - $this->duration = new Duration(self::SECONDS, self::NANOS); - } - - public function testClass() - { - $snippet = $this->snippetFromClass(Duration::class); - $res = $snippet->invoke('duration'); - $this->assertInstanceOf(Duration::class, $res->returnVal()); - } - - public function testClassCast() - { - $snippet = $this->snippetFromClass(Duration::class, 1); - $snippet->addLocal('duration', $this->duration); - - $res = $snippet->invoke(); - $this->assertEquals($this->duration->formatAsString(), $res->output()); - } - - public function testGet() - { - $snippet = $this->snippetFromMethod(Duration::class, 'get'); - $snippet->addLocal('duration', $this->duration); - - $res = $snippet->invoke('res'); - $this->assertEquals($this->duration->get(), $res->returnVal()); - } - - public function testType() - { - $snippet = $this->snippetFromMethod(Duration::class, 'type'); - $snippet->addLocal('duration', $this->duration); - - $res = $snippet->invoke(); - $this->assertEquals(Duration::TYPE, $res->output()); - } - - public function testFormatAsString() - { - $snippet = $this->snippetFromMethod(Duration::class, 'formatAsString'); - $snippet->addLocal('duration', $this->duration); - - $res = $snippet->invoke(); - $this->assertEquals($this->duration->formatAsString(), $res->output()); - } -} diff --git a/Spanner/tests/Snippet/InstanceConfigurationTest.php b/Spanner/tests/Snippet/InstanceConfigurationTest.php index af867e2b8a8c..1e804e5af775 100644 --- a/Spanner/tests/Snippet/InstanceConfigurationTest.php +++ b/Spanner/tests/Snippet/InstanceConfigurationTest.php @@ -17,14 +17,12 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\InstanceConfiguration; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -36,31 +34,32 @@ class InstanceConfigurationTest extends SnippetTestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const CONFIG = 'regional-europe-west'; - private $connection; + private $spannerClient; + private $serializer; private $config; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $this->config = TestHelpers::stub(InstanceConfiguration::class, [ - $this->connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, self::PROJECT, self::CONFIG, - [], - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - ], ['connection', 'lroConnection']); + [], ); } public function testClass() { $snippet = $this->snippetFromClass(InstanceConfiguration::class); + $snippet->addLocal('projectId', self::PROJECT); + $res = $snippet->invoke('configuration'); $this->assertInstanceOf(InstanceConfiguration::class, $res->returnVal()); @@ -73,24 +72,34 @@ public function testClass() public function testCreate() { $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'create'); - $this->connection->createInstanceConfig(Argument::any()) - ->shouldBeCalled() - ->willReturn(['name' => 'operations/foo']); - $dummyConnection = $this->connection->reveal(); + $this->mockSendRequest( + InstanceAdminClient::class, + 'createInstanceConfig', + null, + $this->getOperationResponseMock(), + ); + $baseConfig = new InstanceConfiguration( - $dummyConnection, + $this->requestHandler->reveal(), + $this->serializer, self::PROJECT, self::CONFIG, - [], - $this->prophesize(LongRunningConnectionInterface::class)->reveal() + [] + ); + $this->config->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $this->config->___setProperty( + 'serializer', + $this->serializer ); - $this->config->___setProperty('connection', $dummyConnection); $snippet->addLocal('baseConfig', $baseConfig); $snippet->addLocal('options', []); $snippet->addLocal('instanceConfig', $this->config); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testUpdate() @@ -98,13 +107,16 @@ public function testUpdate() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'update'); $snippet->addLocal('instanceConfig', $this->config); - $this->connection->updateInstanceConfig(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + InstanceAdminClient::class, + 'updateInstanceConfig', + null, + $this->getOperationResponseMock() + ); + + $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->config->___setProperty('serializer', $this->serializer); - $this->config->___setProperty('connection', $this->connection->reveal()); $snippet->invoke(); } @@ -113,10 +125,10 @@ public function testDelete() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'delete'); $snippet->addLocal('instanceConfig', $this->config); - $this->connection->deleteInstanceConfig(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest(InstanceAdminClient::class, 'deleteInstanceConfig', null, null); - $this->config->___setProperty('connection', $this->connection->reveal()); + $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->config->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -139,11 +151,10 @@ public function testInfo() 'displayName' => self::CONFIG ]; - $this->connection->getInstanceConfig(Argument::any()) - ->shouldBeCalled() - ->willReturn($info); + $this->mockSendRequest(InstanceAdminClient::class, 'getInstanceConfig', null, $info); - $this->config->___setProperty('connection', $this->connection->reveal()); + $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->config->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($info, $res->returnVal()); @@ -154,14 +165,18 @@ public function testExists() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'exists'); $snippet->addLocal('configuration', $this->config); - $this->connection->getInstanceConfig(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->mockSendRequest( + InstanceAdminClient::class, + 'getInstanceConfig', + null, + [ 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG), 'displayName' => self::CONFIG - ]); + ] + ); - $this->config->___setProperty('connection', $this->connection->reveal()); + $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->config->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Configuration exists!', $res->output()); @@ -177,11 +192,10 @@ public function testReload() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'reload'); $snippet->addLocal('configuration', $this->config); - $this->connection->getInstanceConfig(Argument::any()) - ->shouldBeCalled() - ->willReturn($info); + $this->mockSendRequest(InstanceAdminClient::class, 'getInstanceConfig', null, $info); - $this->config->___setProperty('connection', $this->connection->reveal()); + $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); + $this->config->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($info, $res->returnVal()); diff --git a/Spanner/tests/Snippet/InstanceTest.php b/Spanner/tests/Snippet/InstanceTest.php index 71cb7897e01c..034711596c9c 100644 --- a/Spanner/tests/Snippet/InstanceTest.php +++ b/Spanner/tests/Snippet/InstanceTest.php @@ -17,21 +17,19 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\Iam\Iam; +use Google\LongRunning\Client\OperationsClient; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\InstanceConfiguration; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -43,7 +41,6 @@ class InstanceTest extends SnippetTestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const INSTANCE = 'my-instance'; @@ -51,26 +48,27 @@ class InstanceTest extends SnippetTestCase const BACKUP = 'my-backup'; const OPERATION = 'my-operation'; - private $connection; + private $spannerClient; + private $serializer; private $instance; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); - $this->instance = TestHelpers::stub(Instance::class, [ - $this->connection->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], + $this->serializer = new Serializer(); + $this->instance = new Instance( + $this->requestHandler->reveal(), + $this->serializer, self::PROJECT, self::INSTANCE - ], ['connection', 'lroConnection']); + ); } public function testClass() { $snippet = $this->snippetFromClass(Instance::class); + $snippet->addLocal('projectId', self::PROJECT); $res = $snippet->invoke('instance'); $this->assertInstanceOf(Instance::class, $res->returnVal()); $this->assertEquals( @@ -91,14 +89,19 @@ public function testCreate() $snippet->addLocal('configuration', $config->reveal()); $snippet->addLocal('instance', $this->instance); - $this->connection->createInstance(Argument::any()) - ->shouldBeCalled() - ->willReturn(['name' => 'operations/foo']); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + InstanceAdminClient::class, + 'createInstance', + null, + $this->getOperationResponseMock() + ); - $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('operation'); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testName() @@ -115,13 +118,18 @@ public function testInfo() $snippet = $this->snippetFromMethod(Instance::class, 'info'); $snippet->addLocal('instance', $this->instance); - $this->connection->getInstance(Argument::any()) - ->shouldBeCalled() - ->willReturn(['nodeCount' => 1]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + InstanceAdminClient::class, + 'getInstance', + null, + ['nodeCount' => 1] + ); - $res = $snippet->invoke(); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke(); $this->assertEquals('1', $res->output()); } @@ -130,13 +138,18 @@ public function testExists() $snippet = $this->snippetFromMethod(Instance::class, 'exists'); $snippet->addLocal('instance', $this->instance); - $this->connection->getInstance(Argument::any()) - ->shouldBeCalled() - ->willReturn(['foo' => 'bar']); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + InstanceAdminClient::class, + 'getInstance', + null, + ['foo' => 'bar'] + ); - $res = $snippet->invoke(); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke(); $this->assertEquals('Instance exists!', $res->output()); } @@ -145,13 +158,18 @@ public function testReload() $snippet = $this->snippetFromMethod(Instance::class, 'reload'); $snippet->addLocal('instance', $this->instance); - $this->connection->getInstance(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn(['nodeCount' => 1]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + InstanceAdminClient::class, + 'getInstance', + null, + ['nodeCount' => 1] + ); - $res = $snippet->invoke('info'); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('info'); $info = $this->instance->info(); $this->assertEquals($info, $res->returnVal()); } @@ -162,13 +180,18 @@ public function testState() $snippet->addLocal('instance', $this->instance); $snippet->addUse(Instance::class); - $this->connection->getInstance(Argument::any()) - ->shouldBeCalledTimes(1) - ->willReturn(['state' => Instance::STATE_READY]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + InstanceAdminClient::class, + 'getInstance', + null, + ['state' => Instance::STATE_READY] + ); - $res = $snippet->invoke(); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke(); $this->assertEquals('Instance is ready!', $res->output()); } @@ -177,14 +200,18 @@ public function testUpdate() $snippet = $this->snippetFromMethod(Instance::class, 'update'); $snippet->addLocal('instance', $this->instance); - $this->connection->updateInstance(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); + $this->mockSendRequest( + InstanceAdminClient::class, + 'updateInstance', + null, + $this->getOperationResponseMock() + ); - $this->instance->___setProperty('connection', $this->connection->reveal()); - $snippet->invoke(); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $snippet->invoke(); } public function testDelete() @@ -192,11 +219,13 @@ public function testDelete() $snippet = $this->snippetFromMethod(Instance::class, 'delete'); $snippet->addLocal('instance', $this->instance); - $this->connection->deleteInstance(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest(InstanceAdminClient::class, 'deleteInstance', null, null); - $this->instance->___setProperty('connection', $this->connection->reveal()); - $snippet->invoke(); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $snippet->invoke(); } public function testCreateDatabase() @@ -204,16 +233,19 @@ public function testCreateDatabase() $snippet = $this->snippetFromMethod(Instance::class, 'createDatabase'); $snippet->addLocal('instance', $this->instance); - $this->connection->createDatabase(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'createDatabase', + null, + $this->getOperationResponseMock() + ); - $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('operation'); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testCreateDatabaseFromBackup() @@ -223,16 +255,19 @@ public function testCreateDatabaseFromBackup() $snippet->addLocal('instance', $this->instance); $snippet->addLocal('backup', $backup); - $this->connection->restoreDatabase(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->mockSendRequest( + DatabaseAdminClient::class, + 'restoreDatabase', + null, + $this->getOperationResponseMock() + ); - $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('operation'); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testDatabase() @@ -250,19 +285,28 @@ public function testDatabases() $snippet = $this->snippetFromMethod(Instance::class, 'databases'); $snippet->addLocal('instance', $this->instance); - $this->connection->listDatabases(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->mockSendRequest( + DatabaseAdminClient::class, + 'listDatabases', + null, + [ 'databases' => [ [ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + 'name' => DatabaseAdminClient::databaseName( + self::PROJECT, + self::INSTANCE, + self::DATABASE + ) ] ] - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + ] + ); - $res = $snippet->invoke('databases'); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('databases'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(Database::class, $res->returnVal()->current()); @@ -283,19 +327,28 @@ public function testBackups() $snippet = $this->snippetFromMethod(Instance::class, 'backups'); $snippet->addLocal('instance', $this->instance); - $this->connection->listBackups(Argument::any()) - ->shouldBeCalled() - ->WillReturn([ + $this->mockSendRequest( + DatabaseAdminClient::class, + 'listBackups', + null, + [ 'backups' => [ [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP) + 'name' => DatabaseAdminClient::backupName( + self::PROJECT, + self::INSTANCE, + self::BACKUP + ) ] ] - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + ] + ); - $res = $snippet->invoke('backups'); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('backups'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(Backup::class, $res->returnVal()->current()); @@ -312,22 +365,27 @@ public function testBackupOperations() $snippet = $this->snippetFromMethod(Instance::class, 'backupOperations'); $snippet->addLocal('instance', $this->instance); - $this->connection->listBackupOperations(Argument::any()) - ->shouldBeCalled() - ->WillReturn([ + $this->mockSendRequest( + DatabaseAdminClient::class, + 'listBackupOperations', + null, + [ 'operations' => [ [ 'name' => $backupOperationName ] ] - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + ] + ); - $res = $snippet->invoke('backupOperations'); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('backupOperations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()->current()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()->current()); } public function testDatabaseOperations() @@ -341,22 +399,27 @@ public function testDatabaseOperations() $snippet = $this->snippetFromMethod(Instance::class, 'databaseOperations'); $snippet->addLocal('instance', $this->instance); - $this->connection->listDatabaseOperations(Argument::any()) - ->shouldBeCalled() - ->WillReturn([ + $this->mockSendRequest( + DatabaseAdminClient::class, + 'listDatabaseOperations', + null, + [ 'operations' => [ [ 'name' => $databaseOperationName ] ] - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + ] + ); - $res = $snippet->invoke('databaseOperations'); + $this->instance->___setProperty( + 'requestHandler', + $this->requestHandler->reveal() + ); + $res = $snippet->invoke('databaseOperations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()->current()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()->current()); } public function testIam() @@ -365,7 +428,7 @@ public function testIam() $snippet->addLocal('instance', $this->instance); $res = $snippet->invoke('iam'); - $this->assertInstanceOf(Iam::class, $res->returnVal()); + $this->assertInstanceOf(IamManager::class, $res->returnVal()); } public function testResumeOperation() @@ -375,7 +438,7 @@ public function testResumeOperation() $snippet->addLocal('operationName', 'foo'); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); $this->assertEquals('foo', $res->returnVal()->name()); } @@ -384,22 +447,21 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Instance::class, 'longRunningOperations'); $snippet->addLocal('instance', $this->instance); - $lroConnection = $this->prophesize(LongRunningConnectionInterface::class); - $lroConnection->operations(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'operations' => [ - [ - 'name' => 'foo' - ] - ] - ]); - - $this->instance->___setProperty('lroConnection', $lroConnection->reveal()); - - $res = $snippet->invoke('operations'); + $this->requestHandler + ->getClientObject(Argument::any()) + ->willReturn(new DatabaseAdminClient()); + $this->requestHandler + ->sendRequest( + Argument::any(), + 'listOperations', + Argument::cetera() + ) + ->willReturn([$this->getOperationResponseMock()]); + + $this->instance->___setProperty('requestHandler', $this->requestHandler->reveal()); + $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); - $this->assertContainsOnlyInstancesOf(LongRunningOperation::class, $res->returnVal()); + $this->assertContainsOnlyInstancesOf(OperationResponse::class, $res->returnVal()); } public function testDatabaseWithDatabaseRole() diff --git a/Spanner/tests/Snippet/SnapshotTest.php b/Spanner/tests/Snippet/SnapshotTest.php index f66b13171519..9fd174da2b66 100644 --- a/Spanner/tests/Snippet/SnapshotTest.php +++ b/Spanner/tests/Snippet/SnapshotTest.php @@ -24,8 +24,6 @@ use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Prophecy\PhpUnit\ProphecyTrait; @@ -35,20 +33,19 @@ class SnapshotTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const TRANSACTION = 'my-transaction'; - private $connection; + private $spannerClient; + private $serializer; private $snapshot; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $operation = $this->prophesize(Operation::class); $session = $this->prophesize(Session::class); diff --git a/Spanner/tests/Snippet/SpannerClientTest.php b/Spanner/tests/Snippet/SpannerClientTest.php index 564f75698cc1..3f9a629f1d9a 100644 --- a/Spanner/tests/Snippet/SpannerClientTest.php +++ b/Spanner/tests/Snippet/SpannerClientTest.php @@ -19,28 +19,27 @@ use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Date; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\InstanceConfiguration; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\SpannerClient; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\PgOid; use Google\Cloud\Spanner\PgJsonb; +use Google\Protobuf\Duration; use Prophecy\Argument; /** @@ -49,22 +48,24 @@ class SpannerClientTest extends SnippetTestCase { use GrpcTestTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const CONFIG = 'foo'; const INSTANCE = 'my-instance'; private $client; - private $connection; + private $spannerClient; + private $serializer; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); - $this->client = TestHelpers::stub(SpannerClient::class); - $this->client->___setProperty('connection', $this->connection->reveal()); + $this->serializer = new Serializer(); + $this->client = new SpannerClient( + [['projectId' => self::PROJECT]], + ['requestHandler', 'serializer'] + ); } public function testClass() @@ -87,16 +88,18 @@ public function testBatch() */ public function testInstanceConfigurations() { - $this->connection->listInstanceConfigs(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->mockSendRequest( + InstanceAdminClient::class, + 'listInstanceConfigs', + null, + [ 'instanceConfigs' => [ ['name' => 'projects/my-awesome-projects/instanceConfigs/foo'], ['name' => 'projects/my-awesome-projects/instanceConfigs/bar'], ] - ]); + ] + ); - $this->client->___setProperty('connection', $this->connection->reveal()); $snippet = $this->snippetFromMethod(SpannerClient::class, 'instanceConfigurations'); $snippet->addLocal('spanner', $this->client); @@ -136,14 +139,16 @@ public function testCreateInstance() $snippet->addLocal('spanner', $this->client); $snippet->addLocal('configuration', $this->client->instanceConfiguration(self::CONFIG)); - $this->connection->createInstance(Argument::any()) - ->shouldBeCalled() - ->willReturn(['name' => 'operations/foo']); + $this->mockSendRequest( + InstanceAdminClient::class, + 'createInstance', + null, + $this->getOperationResponseMock() + ); - $this->client->___setProperty('connection', $this->connection->reveal()); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } /** @@ -170,16 +175,18 @@ public function testInstances() $snippet = $this->snippetFromMethod(SpannerClient::class, 'instances'); $snippet->addLocal('spanner', $this->client); - $this->connection->listInstances(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->mockSendRequest( + InstanceAdminClient::class, + 'listInstances', + null, + [ 'instances' => [ ['name' => InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)], ['name' => InstanceAdminClient::instanceName(self::PROJECT, 'bar')] ] - ]); + ] + ); - $this->client->___setProperty('connection', $this->connection->reveal()); $res = $snippet->invoke('instances'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); @@ -273,7 +280,7 @@ public function testResumeOperation() $snippet->addLocal('operationName', 'operations/foo'); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(LongRunningOperation::class, $res->returnVal()); + $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } public function testEmulator() diff --git a/Spanner/tests/Snippet/StructTypeTest.php b/Spanner/tests/Snippet/StructTypeTest.php index bbd1f7bc08ef..d0d5c5427ceb 100644 --- a/Spanner/tests/Snippet/StructTypeTest.php +++ b/Spanner/tests/Snippet/StructTypeTest.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; @@ -28,8 +27,7 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructType; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -40,15 +38,14 @@ class StructTypeTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; const INSTANCE = 'my-instance'; - private $connection; + private $spannerClient; + private $serializer; private $database; private $type; @@ -76,16 +73,15 @@ public function setUp(): void $sessionPool->setDatabase(Argument::any()) ->willReturn(null); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $this->database = TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE, $sessionPool->reveal() - ], ['operation']); + ], ['operation', 'requestHandler', 'serializer']); $this->type = new StructType; } @@ -96,12 +92,16 @@ public function testExecuteStruct() [ 'name' => 'firstName', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + "typeAnnotation" => 0, + "protoTypeFqn" => "" ] ], [ 'name' => 'lastName', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + "typeAnnotation" => 0, + "protoTypeFqn" => "" ] ] ]; @@ -111,29 +111,24 @@ public function testExecuteStruct() 'Testuser' ]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', 'SELECT @userStruct.firstName, @userStruct.lastName'), - Argument::withEntry('params', [ - 'userStruct' => $values - ]), - Argument::withEntry('paramTypes', [ - 'userStruct' => [ - 'code' => Database::TYPE_STRUCT, - 'structType' => [ + $this->spannerClient->executeStreamingSql( + function ($args) use ($fields, $values) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals('SELECT @userStruct.firstName, @userStruct.lastName', $args->getSql()); + $this->assertEquals($message['params']['userStruct'], $values); + $this->assertEquals($message['paramTypes']['userStruct']['structType']['fields'], $fields); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ 'fields' => $fields ] - ] + ], + 'values' => $values ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => $fields - ] - ], - 'values' => $values - ])); - - $this->refreshOperation($this->database, $this->connection->reveal()); + ); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromClass(StructType::class); $snippet->replace('$database = $spanner->connect(\'my-instance\', \'my-database\');', ''); diff --git a/Spanner/tests/Snippet/StructValueTest.php b/Spanner/tests/Snippet/StructValueTest.php index 64eab73f2f23..8eecb4e434c0 100644 --- a/Spanner/tests/Snippet/StructValueTest.php +++ b/Spanner/tests/Snippet/StructValueTest.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; @@ -27,8 +26,7 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructValue; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -39,15 +37,14 @@ class StructValueTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; const INSTANCE = 'my-instance'; - private $connection; + private $spannerClient; + private $serializer; private $database; private $value; @@ -75,12 +72,11 @@ public function setUp(): void $sessionPool->setDatabase(Argument::any()) ->willReturn(null); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $this->database = TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE, $sessionPool->reveal() @@ -95,16 +91,23 @@ public function testConstructor() [ 'name' => 'foo', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ 'name' => 'foo', 'type' => [ - 'code' => Database::TYPE_INT64 + 'code' => Database::TYPE_INT64, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ + 'name'=> '', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ] ]; @@ -115,29 +118,40 @@ public function testConstructor() 'this field is unnamed' ]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', 'SELECT * FROM UNNEST(ARRAY(SELECT @structParam))'), - Argument::withEntry('params', [ - 'structParam' => $values - ]), - Argument::withEntry('paramTypes', [ - 'structParam' => [ - 'code' => Database::TYPE_STRUCT, - 'structType' => [ + $this->spannerClient->executeStreamingSql( + function ($args) use ($values, $fields) { + $this->assertEquals( + $args->getSql(), + 'SELECT * FROM UNNEST(ARRAY(SELECT @structParam))' + ); + $message = $this->serializer->encodeMessage($args); + $this->assertEquals($message['params'], ['structParam' => $values]); + $this->assertEquals( + $message['paramTypes'], + [ + 'structParam' => [ + 'code' => Database::TYPE_STRUCT, + 'structType' => [ + 'fields' => $fields + ], + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' + ] + ] + ); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ 'fields' => $fields ] - ] + ], + 'values' => $values ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => $fields - ] - ], - 'values' => $values - ])); + ); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromClass(StructValue::class); $snippet->replace('$database = $spanner->connect(\'my-instance\', \'my-database\');', ''); diff --git a/Spanner/tests/Snippet/TransactionTest.php b/Spanner/tests/Snippet/TransactionTest.php index 48027c6c44c8..38f03431c947 100644 --- a/Spanner/tests/Snippet/TransactionTest.php +++ b/Spanner/tests/Snippet/TransactionTest.php @@ -27,12 +27,11 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\StructType; use Google\Cloud\Spanner\StructValue; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -42,21 +41,20 @@ class TransactionTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; const TRANSACTION = 'my-transaction'; - private $connection; + private $spannerClient; + private $serializer; private $transaction; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $operation = $this->prophesize(Operation::class); $session = $this->prophesize(Session::class); $session->info() @@ -100,11 +98,12 @@ public function testClassReturnTransaction() public function testExecute() { - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator() + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation($this->transaction, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMagicMethod(Transaction::class, 'execute'); $snippet->addLocal('transaction', $this->transaction); @@ -115,11 +114,12 @@ public function testExecute() public function testExecuteUpdate() { - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(true)); + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator(true) + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation($this->transaction, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Transaction::class, 'executeUpdate'); $snippet->addLocal('transaction', $this->transaction); @@ -140,37 +140,33 @@ public function testExecuteUpdateWithStruct() [ "name" => "Title", "type" => [ - "code" => Database::TYPE_STRING + "code" => Database::TYPE_STRING, + "typeAnnotation" => 0, + "protoTypeFqn" => "" ] ], [ "name" => "Content", "type" => [ - "code" => Database::TYPE_STRING + "code" => Database::TYPE_STRING, + "typeAnnotation" => 0, + "protoTypeFqn" => "" ] ] ]; - $this->connection->executeStreamingSql( - Argument::allOf( - Argument::withEntry('sql', $expectedSql), - Argument::withEntry('params', $expectedParams), - Argument::withEntry( - 'paramTypes', - Argument::withEntry( - 'post', - Argument::withEntry( - 'structType', - Argument::withEntry('fields', Argument::is($expectedStructData)) - ) - ) - ) - ) - ) - ->shouldBeCalled() - ->willReturn($this->resultGenerator(true)); + $this->spannerClient->executeStreamingSql( + function ($args) use ($expectedSql, $expectedParams, $expectedStructData) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals($expectedSql, $args->getSql()); + $this->assertEquals($message['params'], $expectedParams); + $this->assertEquals($message['paramTypes']['post']['structType']['fields'], $expectedStructData); + return true; + }, + $this->resultGenerator(true) + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation($this->transaction, $this->requestHandler->reveal(), $this->serializer); $snippet = $this->snippetFromMethod(Transaction::class, 'executeUpdate', 1); $snippet->addUse(Database::class); @@ -185,9 +181,9 @@ public function testExecuteUpdateWithStruct() public function testExecuteUpdateBatch() { - $this->connection->executeBatchDml(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->executeBatchDml( + null, + [ 'resultSets' => [ [ 'stats' => [ @@ -195,9 +191,14 @@ public function testExecuteUpdateBatch() ] ] ] - ]); + ] + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'executeUpdateBatch'); $snippet->addLocal('transaction', $this->transaction); @@ -208,17 +209,22 @@ public function testExecuteUpdateBatch() public function testExecuteUpdateBatchError() { - $this->connection->executeBatchDml(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->executeBatchDml( + null, + [ 'resultSets' => [], 'status' => [ 'code' => 3, 'message' => 'foo' ] - ]); + ] + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'executeUpdateBatch'); $snippet->addLocal('transaction', $this->transaction); @@ -229,11 +235,16 @@ public function testExecuteUpdateBatchError() public function testRead() { - $this->connection->streamingRead(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->streamingRead( + null, + $this->resultGenerator() + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMagicMethod(Transaction::class, 'read'); $snippet->addLocal('transaction', $this->transaction); @@ -256,7 +267,11 @@ public function testInsert() $snippet = $this->snippetFromMethod(Transaction::class, 'insert'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -270,7 +285,11 @@ public function testInsertBatch() $snippet = $this->snippetFromMethod(Transaction::class, 'insertBatch'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -283,7 +302,11 @@ public function testUpdate() $snippet = $this->snippetFromMethod(Transaction::class, 'update'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -297,7 +320,11 @@ public function testUpdateBatch() $snippet = $this->snippetFromMethod(Transaction::class, 'updateBatch'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -310,7 +337,11 @@ public function testInsertOrUpdate() $snippet = $this->snippetFromMethod(Transaction::class, 'insertOrUpdate'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -321,12 +352,20 @@ public function testInsertOrUpdate() public function testInsertOrUpdateBatch() { - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'insertOrUpdateBatch'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -339,7 +378,11 @@ public function testReplace() $snippet = $this->snippetFromMethod(Transaction::class, 'replace'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -353,7 +396,11 @@ public function testReplaceBatch() $snippet = $this->snippetFromMethod(Transaction::class, 'replaceBatch'); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -367,7 +414,11 @@ public function testDelete() $snippet->addUse(KeySet::class); $snippet->addLocal('mutationGroup', $this->transaction); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $res = $snippet->invoke(); @@ -377,10 +428,13 @@ public function testDelete() public function testRollback() { - $this->connection->rollback(Argument::any()) - ->shouldBeCalled(); + $this->mockSendRequest(SpannerClient::class, 'rollback', null, null); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'rollback'); $snippet->addLocal('transaction', $this->transaction); @@ -390,13 +444,18 @@ public function testRollback() public function testCommit() { - $this->connection->commit(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->commit( + null, + [ 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() - ]); + ] + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'commit'); $snippet->addLocal('transaction', $this->transaction); @@ -407,14 +466,19 @@ public function testCommit() public function testGetCommitStats() { $expectedCommitStats = new CommitStats(['mutation_count' => 4]); - $this->connection->commit(Argument::any()) - ->shouldBeCalled() - ->willReturn([ + $this->spannerClient->commit( + null, + [ 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString(), 'commitStats' => $expectedCommitStats, - ]); + ] + ); - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->refreshOperation( + $this->transaction, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet = $this->snippetFromMethod(Transaction::class, 'getCommitStats'); $snippet->addLocal('transaction', $this->transaction); diff --git a/Spanner/tests/Snippet/TransactionalReadMethodsTest.php b/Spanner/tests/Snippet/TransactionalReadMethodsTest.php index 5ad92609999c..b0e0fe5f4e98 100644 --- a/Spanner/tests/Snippet/TransactionalReadMethodsTest.php +++ b/Spanner/tests/Snippet/TransactionalReadMethodsTest.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; @@ -29,11 +28,9 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; -use Google\Cloud\Spanner\V1\Gapic\SpannerGapicClient; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -49,9 +46,7 @@ class TransactionalReadMethodsTest extends SnippetTestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; @@ -59,7 +54,8 @@ class TransactionalReadMethodsTest extends SnippetTestCase const TRANSACTION = 'my-transaction'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; - private $connection; + private $spannerClient; + private $serializer; private $session; private $operation; @@ -71,12 +67,14 @@ public function setUp(): void { parent::setUpBeforeClass(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $this->session = $this->prophesize(Session::class); $this->session->info() ->willReturn([ 'databaseName' => 'database' ]); + $this->session->name() + ->willReturn('sessionName'); $this->operation = $this->prophesize(Operation::class); } @@ -97,25 +95,28 @@ public function testExecute($localName, $client, $snippet) { $this->checkAndSkipGrpcTests(); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator([ + $this->spannerClient->executeStreamingSql( + null, + $this->resultGenerator([ 'metadata' => [ 'rowType' => [ 'fields' => [ [ 'name' => 'loginCount', - 'type' => [ - 'code' => Database::TYPE_INT64 - ] + 'type' => ['code' => Database::TYPE_INT64] ] ] ] ], 'values' => [0] - ])); + ]) + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation( + $client, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet->addLocal($localName, $client); @@ -140,37 +141,35 @@ public function testExecuteWithParameterType($localName, $client, $snippet) { $this->checkAndSkipGrpcTests(); - $this->connection->executeStreamingSql(Argument::that(function ($arg) { - if (!isset($arg['params'])) { - return false; - } - - if (!isset($arg['paramTypes'])) { - return false; - } - - if ($arg['paramTypes']['timestamp']['code'] !== Database::TYPE_TIMESTAMP) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'timestamp', - 'type' => [ - 'code' => Database::TYPE_TIMESTAMP + $this->spannerClient->executeStreamingSql( + function ($args) { + $message = $this->serializer->encodeMessage($args); + $this->assertTrue(isset($message['params'])); + $this->assertTrue(isset($message['paramTypes'])); + $this->assertEquals( + $message['paramTypes']['timestamp']['code'], + Database::TYPE_TIMESTAMP + ); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'timestamp', + 'type' => [ + 'code' => Database::TYPE_TIMESTAMP + ] ] ] ] - ] - ], - 'values' => [null] - ])); + ], + 'values' => [null] + ]) + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation($client, $this->requestHandler->reveal(), $this->serializer); $snippet->addLocal($localName, $client); @@ -195,44 +194,42 @@ public function testExecuteWithEmptyArray($localName, $client, $snippet) { $this->checkAndSkipGrpcTests(); - $this->connection->executeStreamingSql(Argument::that(function ($arg) { - if (!isset($arg['params'])) { - return false; - } - - if (!isset($arg['paramTypes'])) { - return false; - } - - if ($arg['paramTypes']['emptyArrayOfIntegers']['code'] !== Database::TYPE_ARRAY) { - return false; - } - - if ($arg['paramTypes']['emptyArrayOfIntegers']['arrayElementType']['code'] !== Database::TYPE_INT64) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'numbers', - 'type' => [ - 'code' => Database::TYPE_ARRAY, - 'arrayElementType' => [ - 'code' => Database::TYPE_INT64 + $this->spannerClient->executeStreamingSql( + function ($args) { + $message = $this->serializer->encodeMessage($args); + $this->assertTrue(isset($message['params'])); + $this->assertTrue(isset($message['paramTypes'])); + $this->assertEquals( + $message['paramTypes']['emptyArrayOfIntegers']['code'], + Database::TYPE_ARRAY + ); + $this->assertEquals( + $message['paramTypes']['emptyArrayOfIntegers']['arrayElementType']['code'], + Database::TYPE_INT64 + ); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'numbers', + 'type' => [ + 'code' => Database::TYPE_ARRAY, + 'arrayElementType' => [ + 'code' => Database::TYPE_INT64 + ] ] ] ] ] - ] - ], - 'values' => [[]] - ])); + ], + 'values' => [[]] + ]) + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation($client, $this->requestHandler->reveal(), $this->serializer); $snippet->addLocal($localName, $client); @@ -261,12 +258,16 @@ public function testExecuteStruct($localName, $client, $snippet) [ 'name' => 'firstName', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ 'name' => 'lastName', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ] ]; @@ -276,29 +277,31 @@ public function testExecuteStruct($localName, $client, $snippet) 'Testuser' ]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', 'SELECT @userStruct.firstName, @userStruct.lastName'), - Argument::withEntry('params', [ - 'userStruct' => $values - ]), - Argument::withEntry('paramTypes', [ - 'userStruct' => [ - 'code' => Database::TYPE_STRUCT, - 'structType' => [ + $this->spannerClient->executeStreamingSql( + function ($args) use ($values, $fields) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals($message['sql'], 'SELECT @userStruct.firstName, @userStruct.lastName'); + $this->assertEquals( + $message['params'], + ['userStruct' => $values] + ); + $this->assertEquals( + $message['paramTypes']['userStruct']['structType']['fields'], + $fields + ); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ 'fields' => $fields ] - ] + ], + 'values' => $values ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => $fields - ] - ], - 'values' => $values - ])); + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation($client, $this->requestHandler->reveal(), $this->serializer); $snippet->addLocal($localName, $client); @@ -327,16 +330,23 @@ public function testExecuteStructDuplicateAndUnnamedFields($localName, $client, [ 'name' => 'foo', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ 'name' => 'foo', 'type' => [ - 'code' => Database::TYPE_INT64 + 'code' => Database::TYPE_INT64, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ + 'name' => '', 'type' => [ - 'code' => Database::TYPE_STRING + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ] ]; @@ -347,29 +357,44 @@ public function testExecuteStructDuplicateAndUnnamedFields($localName, $client, 'this field is unnamed' ]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', 'SELECT * FROM UNNEST(ARRAY(SELECT @structParam))'), - Argument::withEntry('params', [ - 'structParam' => $values - ]), - Argument::withEntry('paramTypes', [ - 'structParam' => [ - 'code' => Database::TYPE_STRUCT, - 'structType' => [ + $this->spannerClient->executeStreamingSql( + function ($args) use ($values, $fields) { + $message = $this->serializer->encodeMessage($args); + $this->assertEquals( + $message['sql'], + 'SELECT * FROM UNNEST(ARRAY(SELECT @structParam))' + ); + $this->assertEquals($message['params'], ['structParam' => $values]); + $this->assertEquals( + $message['paramTypes'], + [ + 'structParam' => [ + 'code' => Database::TYPE_STRUCT, + 'structType' => [ + 'fields' => $fields + ], + 'typeAnnotation' => 0, + 'protoTypeFqn' => '', + ] + ] + ); + return true; + }, + $this->resultGenerator([ + 'metadata' => [ + 'rowType' => [ 'fields' => $fields ] - ] + ], + 'values' => $values ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => $fields - ] - ], - 'values' => $values - ])); + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation( + $client, + $this->requestHandler->reveal(), + $this->serializer + ); $snippet->addLocal($localName, $client); @@ -396,9 +421,9 @@ public function testRead($localName, $client, $snippet) { $this->checkAndSkipGrpcTests(); - $this->connection->streamingRead(Argument::any()) - ->shouldBeCalled() - ->willReturn($this->resultGenerator([ + $this->spannerClient->streamingRead( + null, + $this->resultGenerator([ 'metadata' => [ 'rowType' => [ 'fields' => [ @@ -412,9 +437,10 @@ public function testRead($localName, $client, $snippet) ] ], 'rows' => [0] - ])); + ]) + ); - $this->refreshOperation($client, $this->connection->reveal()); + $this->refreshOperation($client, $this->requestHandler->reveal(), $this->serializer); $snippet->addLocal($localName, $client); @@ -436,10 +462,9 @@ private function setupDatabase() ->willReturn(null); return \Google\Cloud\Core\Testing\TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $this->requestHandler->reveal(), + $this->serializer, $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE, $sessionPool->reveal() @@ -473,11 +498,11 @@ private function setupSnapshot() private function setupBatch() { - $sessData = SpannerGapicClient::parseName(self::SESSION, 'session'); + $sessData = SpannerClient::parseName(self::SESSION, 'session'); $this->session->name()->willReturn(self::SESSION); $this->session->info()->willReturn($sessData + [ 'name' => self::SESSION, - 'databaseName' => SpannerGapicClient::databaseName( + 'databaseName' => SpannerClient::databaseName( self::PROJECT, self::INSTANCE, self::DATABASE @@ -485,7 +510,7 @@ private function setupBatch() ]); return \Google\Cloud\Core\Testing\TestHelpers::stub(BatchSnapshot::class, [ - new Operation($this->connection->reveal(), false), + new Operation($this->requestHandler->reveal(), $this->serializer, false), $this->session->reveal(), [ 'id' => self::TRANSACTION, diff --git a/Spanner/tests/StubCreationTrait.php b/Spanner/tests/StubCreationTrait.php deleted file mode 100644 index 5c0d328171f6..000000000000 --- a/Spanner/tests/StubCreationTrait.php +++ /dev/null @@ -1,38 +0,0 @@ -prophesize(ConnectionInterface::class); - $c->deleteSession(Argument::any())->willReturn([]); - - return $c; - } -} diff --git a/Spanner/tests/System/AdminTest.php b/Spanner/tests/System/AdminTest.php index 9409f94916c9..75af77638313 100644 --- a/Spanner/tests/System/AdminTest.php +++ b/Spanner/tests/System/AdminTest.php @@ -19,6 +19,7 @@ use Google\Cloud\Core\Exception\FailedPreconditionException; use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; @@ -63,7 +64,7 @@ public function testInstance() 'processingUnits' => $processingUnits, ]); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); $op->pollUntilComplete(); $instance = $client->instance(self::INSTANCE_NAME); @@ -95,7 +96,7 @@ public function testDatabase() $dbName = uniqid(self::TESTING_PREFIX); $op = $instance->createDatabase($dbName); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); $db = $op->pollUntilComplete(); $this->assertInstanceOf(Database::class, $db); @@ -138,7 +139,7 @@ public function testDatabaseDropProtection() $dbName = uniqid(self::TESTING_PREFIX); $op = $instance->createDatabase($dbName); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); $db = $op->pollUntilComplete(); $this->assertInstanceOf(Database::class, $db); @@ -203,7 +204,7 @@ public function testCreateCustomerManagedInstanceConfiguration() $replicas[array_rand($replicas)]['defaultLeaderLocation'] = true; $op = $customConfiguration->create($baseConfig, $replicas); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); $op->pollUntilComplete(); $this->assertTrue($customConfiguration->exists()); @@ -289,7 +290,7 @@ public function testPgDatabase() 'databaseDialect' => DatabaseDialect::POSTGRESQL ]); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); $db = $op->pollUntilComplete(); $this->assertInstanceOf(Database::class, $db); diff --git a/Spanner/tests/System/BackupTest.php b/Spanner/tests/System/BackupTest.php index 5d8633c6e7e0..575093f24bc3 100644 --- a/Spanner/tests/System/BackupTest.php +++ b/Spanner/tests/System/BackupTest.php @@ -19,7 +19,7 @@ use Google\Cloud\Core\Exception\BadRequestException; use Google\Cloud\Core\Exception\ConflictException; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\ApiCore\OperationResponse; use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupEncryptionConfig; use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; @@ -124,7 +124,6 @@ public function testListAllInstances() public function testCreateBackup() { $expireTime = new \DateTime('+7 hours'); - $versionTime = new \DateTime('-5 seconds'); $encryptionConfig = [ 'encryptionType' => CreateBackupEncryptionConfig\EncryptionType::GOOGLE_DEFAULT_ENCRYPTION, ]; @@ -134,7 +133,6 @@ public function testCreateBackup() self::$createTime1 = gmdate('"Y-m-d\TH:i:s\Z"'); $op = $backup->create(self::$dbName1, $expireTime, [ - 'versionTime' => $versionTime, 'encryptionConfig' => $encryptionConfig, ]); self::$backupOperationName = $op->name(); @@ -220,6 +218,9 @@ public function testCreateBackupInvalidArgument() $this->assertFalse($backup->exists()); } + /** + * @depends testCreateBackup + */ public function testCancelBackupOperation() { $expireTime = new \DateTime('+7 hours'); @@ -335,6 +336,9 @@ public function testUpdateExpirationTimeFailed() $this->assertEquals($currentExpireTime, $backup->info()['expireTime']); } + /** + * @depends testCreateBackup + */ public function testListAllBackups() { $allBackups = iterator_to_array(self::$instance->backups(), false); @@ -347,6 +351,9 @@ public function testListAllBackups() $this->assertContainsOnlyInstancesOf(Backup::class, $allBackups); } + /** + * @depends testCreateBackup + */ public function testListAllBackupsContainsName() { $backups = iterator_to_array(self::$instance->backups(['filter' => 'name:' . self::$backupId1])); @@ -354,6 +361,9 @@ public function testListAllBackupsContainsName() $this->assertEquals(self::$backupId1, DatabaseAdminClient::parseName($backups[0]->info()['name'])['backup']); } + /** + * @depends testCreateBackup + */ public function testListAllBackupsReady() { $backups = iterator_to_array(self::$instance->backups(['filter'=>'state:READY'])); @@ -366,6 +376,9 @@ public function testListAllBackupsReady() $this->assertTrue(in_array(self::fullyQualifiedBackupName(self::$backupId1), $backupNames)); } + /** + * @depends testCreateBackup + */ public function testListAllBackupsOfDatabase() { $database = self::$instance->database(self::$dbName1); @@ -378,9 +391,12 @@ public function testListAllBackupsOfDatabase() } } + /** + * @depends testCreateBackup + */ public function testListAllBackupsCreatedAfterTimestamp() { - $filter = sprintf("create_time >= %s", self::$createTime2); + $filter = sprintf("create_time >= %s", self::$createTime1); $backups = iterator_to_array(self::$instance->backups(['filter'=>$filter])); @@ -389,10 +405,12 @@ public function testListAllBackupsCreatedAfterTimestamp() $backupNames[] = $b->name(); } $this->assertTrue(count($backupNames) > 0); - $this->assertFalse(in_array(self::fullyQualifiedBackupName(self::$backupId1), $backupNames)); - $this->assertTrue(in_array(self::fullyQualifiedBackupName(self::$backupId2), $backupNames)); + $this->assertTrue(in_array(self::fullyQualifiedBackupName(self::$backupId1), $backupNames)); } + /** + * @depends testCreateBackup + */ public function testListAllBackupsExpireBeforeTimestamp() { $filter = "expire_time < " . gmdate('"Y-m-d\TH:i:s\Z"', strtotime('+9 hours')); @@ -429,6 +447,9 @@ public function testListAllBackupsWithSizeGreaterThanSomeBytes() $this->assertTrue(in_array(self::fullyQualifiedBackupName(self::$backupId2), $backupNames)); } + /** + * @depends testCancelBackupOperation + */ public function testPagination() { $backupsfirstPage = self::$instance->backups(['pageSize' => 1]); @@ -445,6 +466,9 @@ public function testPagination() $this->assertEquals(2, count($backupsPageSizeTwo)); } + /** + * @depends testRestoreToNewDatabase + */ public function testListAllBackupOperations() { $backupOps = iterator_to_array($this::$instance->backupOperations()); @@ -454,7 +478,7 @@ public function testListAllBackupOperations() }, $backupOps); $this->assertTrue(count($backupOps) > 0); - $this->assertContainsOnlyInstancesOf(LongRunningOperation::class, $backupOps); + $this->assertContainsOnlyInstancesOf(OperationResponse::class, $backupOps); $this->assertTrue(in_array(self::$backupOperationName, $backupOpsNames)); } @@ -557,6 +581,9 @@ public function testRestoreToNewDatabase() $this->assertArrayHasKey('startTime', $metadata['progress']); } + /** + * @depends testRestoreToNewDatabase + */ public function testRestoreAppearsInListDatabaseOperations() { $databaseOps = iterator_to_array($this::$instance->databaseOperations()); @@ -565,10 +592,13 @@ public function testRestoreAppearsInListDatabaseOperations() }, $databaseOps); $this->assertTrue(count($databaseOps) > 0); - $this->assertContainsOnlyInstancesOf(LongRunningOperation::class, $databaseOps); + $this->assertContainsOnlyInstancesOf(OperationResponse::class, $databaseOps); $this->assertTrue(in_array(self::$restoreOperationName, $databaseOpsNames)); } + /** + * @depends testCreateBackup + */ public function testRestoreBackupToAnExistingDatabase() { $existingDb = self::$instance->database(self::$dbName2); diff --git a/Spanner/tests/System/BatchTest.php b/Spanner/tests/System/BatchTest.php index 2d90a07e6c08..896d21bd6d85 100644 --- a/Spanner/tests/System/BatchTest.php +++ b/Spanner/tests/System/BatchTest.php @@ -121,7 +121,6 @@ public function testBatch() $partitions = $snapshot->partitionQuery($query, ['parameters' => $parameters]); $this->assertEquals(count($resultSet), $this->executePartitions($batch, $snapshot, $partitions)); - // ($table, KeySet $keySet, array $columns, array $options = []) $keySet = new KeySet([ 'ranges' => [ new KeyRange([ @@ -182,60 +181,6 @@ public function testBatchWithDbRole($dbRole, $expected) $snapshot->close(); } - public function testBatchWithDataBoostEnabled() - { - // Emulator does not support dataBoostEnabled - $this->skipEmulatorTests(); - - $query = 'SELECT - id, - decade - FROM ' . self::$tableName . ' - WHERE - decade > @earlyBound - AND - decade < @lateBound'; - - $parameters = [ - 'earlyBound' => 1960, - 'lateBound' => 1980 - ]; - - $resultSet = iterator_to_array(self::$database->execute($query, ['parameters' => $parameters])); - - $batch = self::$client->batch(self::INSTANCE_NAME, self::$dbName); - $string = $batch->snapshot()->serialize(); - - $snapshot = $batch->snapshotFromString($string); - - $partitions = $snapshot->partitionQuery($query, [ - 'parameters' => $parameters, - 'dataBoostEnabled' => true - ]); - $this->assertEquals(count($resultSet), $this->executePartitions($batch, $snapshot, $partitions)); - - $keySet = new KeySet([ - 'ranges' => [ - new KeyRange([ - 'start' => $parameters['earlyBound'], - 'startType' => KeyRange::TYPE_OPEN, - 'end' => $parameters['lateBound'], - 'endType' => KeyRange::TYPE_OPEN - ]) - ] - ]); - - $partitions = $snapshot->partitionRead( - self::$tableName, - $keySet, - ['id', 'decade'], - ['dataBoostEnabled' => true] - ); - $this->assertEquals(count($resultSet), $this->executePartitions($batch, $snapshot, $partitions)); - - $snapshot->close(); - } - private function executePartitions(BatchClient $client, BatchSnapshot $snapshot, array $partitions) { $partitionResultSet = []; diff --git a/Spanner/tests/System/PgReadTest.php b/Spanner/tests/System/PgReadTest.php index 30ce4c48dd14..af14f9910966 100644 --- a/Spanner/tests/System/PgReadTest.php +++ b/Spanner/tests/System/PgReadTest.php @@ -312,7 +312,7 @@ public function testReadWithLimit() }; $limitCount = count(iterator_to_array($res(10))); - $unlimitCount = count(iterator_to_array($res(null))); + $unlimitCount = count(iterator_to_array($res(0))); $this->assertEquals(10, $limitCount); $this->assertNotEquals($limitCount, $unlimitCount); @@ -331,7 +331,7 @@ public function testReadOverIndexWithLimit() }; $limitCount = count(iterator_to_array($res(10))); - $unlimitCount = count(iterator_to_array($res(null))); + $unlimitCount = count(iterator_to_array($res(0))); $this->assertEquals(10, $limitCount); $this->assertNotEquals($limitCount, $unlimitCount); diff --git a/Spanner/tests/System/ReadTest.php b/Spanner/tests/System/ReadTest.php index a6ebcc47a554..95609aac31f9 100644 --- a/Spanner/tests/System/ReadTest.php +++ b/Spanner/tests/System/ReadTest.php @@ -349,7 +349,7 @@ public function testReadWithLimit() }; $limitCount = count(iterator_to_array($res(10))); - $unlimitCount = count(iterator_to_array($res(null))); + $unlimitCount = count(iterator_to_array($res(0))); $this->assertEquals(10, $limitCount); $this->assertNotEquals($limitCount, $unlimitCount); @@ -371,7 +371,7 @@ public function testReadOverIndexWithLimit() }; $limitCount = count(iterator_to_array($res(10))); - $unlimitCount = count(iterator_to_array($res(null))); + $unlimitCount = count(iterator_to_array($res(0))); $this->assertEquals(10, $limitCount); $this->assertNotEquals($limitCount, $unlimitCount); diff --git a/Spanner/tests/System/SnapshotTest.php b/Spanner/tests/System/SnapshotTest.php index e41dc3c3cb8d..f4c11db9d344 100644 --- a/Spanner/tests/System/SnapshotTest.php +++ b/Spanner/tests/System/SnapshotTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\System; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Timestamp; +use Google\Protobuf\Duration; /** * @group spanner @@ -162,7 +162,7 @@ public function testSnapshotExactStaleness() $newRow['number'] = 2; $db->replace(self::$tableName, $newRow); - $duration = new Duration(1); + $duration = new Duration(['seconds' => 1, 'nanos' => 0]); $snapshot = $db->snapshot([ 'exactStaleness' => $duration, @@ -197,7 +197,7 @@ public function testSnapshotMaxStaleness() $newRow['number'] = 2; $db->replace(self::$tableName, $newRow); - $duration = new Duration(1); + $duration = new Duration(['seconds' => 1, 'nanos' => 0]); $snapshot = $db->snapshot([ 'maxStaleness' => $duration, @@ -233,7 +233,7 @@ public function testSnapshotMaxStalenessFails() $db = self::$database; $db->snapshot([ - 'maxStaleness' => new Duration(1) + 'maxStaleness' => new Duration(['seconds' => 1, 'nanos' => 0]) ]); } diff --git a/Spanner/tests/Unit/Admin/Database/V1/Client/DatabaseAdminClientTest.php b/Spanner/tests/Unit/Admin/Database/V1/Client/DatabaseAdminClientTest.php index 76c5c46c6a10..85b63a46f722 100644 --- a/Spanner/tests/Unit/Admin/Database/V1/Client/DatabaseAdminClientTest.php +++ b/Spanner/tests/Unit/Admin/Database/V1/Client/DatabaseAdminClientTest.php @@ -1,6 +1,6 @@ checkAndSkipGrpcTests(); - $this->connection = $this->prophesize(ConnectionInterface::class); - $this->instance = $this->prophesize(Instance::class); + $this->databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]); + $this->database = $this->prophesize(Database::class); - $this->database->name()->willReturn( - DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE) - ); - $this->instance->name()->willReturn(InstanceAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE)); + $this->database->name()->willReturn(DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE)); + + $this->instance = $this->prophesize(Instance::class); + $this->instance->name()->willReturn(DatabaseAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE)); $this->instance->database(Argument::any())->willReturn($this->database); - $this->lro = $this->prophesize(LongRunningConnectionInterface::class); - $this->lroCallables = []; - $this->expireTime = new \DateTime("+ 7 hours"); - $this->createTime = $this->expireTime; - $this->versionTime = new \DateTime("- 2 hours"); - - $args=[ - $this->connection->reveal(), - $this->instance->reveal(), - $this->lro->reveal(), - $this->lroCallables, - self::PROJECT_ID, - self::BACKUP - ]; - $props = [ - 'instance', 'connection' - ]; - $this->backup = TestHelpers::stub(Backup::class, $args, $props); - // copiedBackup will contain a mock of the backup object where - // $backup will be copied into - $copyArgs = $args; - $copyArgs[5] = self::COPIED_BACKUP; - $this->copiedBackup = TestHelpers::stub(Backup::class, $copyArgs, $props); + $this->operationResponse = $this->prophesize(OperationResponse::class); + $this->operationResponse->withResultFunction(Argument::type('callable')) + ->willReturn($this->operationResponse->reveal()); + + $this->expireTime = new DateTime("+7 hours"); + $this->versionTime = new DateTime("-2 hours"); } public function testName() { + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); + $this->assertEquals( DatabaseAdminClient::backupName(self::PROJECT_ID, self::INSTANCE, self::BACKUP), - $this->backup->name() + $backup->name() ); } public function testCreate() { - $this->connection->createBackup(Argument::allOf( - Argument::withEntry('instance', InstanceAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE)), - Argument::withEntry('backupId', self::BACKUP), - Argument::withEntry('backup', [ - 'database' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE), - 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z'), - ]), - Argument::withEntry('versionTime', $this->versionTime->format('Y-m-d\TH:i:s.u\Z')) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - - $this->backup->___setProperty('connection', $this->connection->reveal()); - $op = $this->backup->create(self::DATABASE, $this->expireTime, [ + $expected = [ + 'parent' => DatabaseAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE), + 'database' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE), + 'expire_time' => $this->expireTime->format('U'), + 'version_time' => $this->versionTime->format('U'), + ]; + $this->databaseAdminClient->createBackup( + Argument::that(function (CreateBackupRequest $request) use ($expected) { + $this->assertEquals($expected['parent'], $request->getParent()); + $this->assertEquals(self::BACKUP, $request->getBackupId()); + $this->assertEquals($expected['database'], $request->getBackup()->getDatabase()); + $this->assertEquals($expected['expire_time'], $request->getBackup()->getExpireTime()->getSeconds()); + $this->assertEquals($expected['version_time'], $request->getBackup()->getVersionTime()->getSeconds()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); + + $operation = $backup->create(self::DATABASE, $this->expireTime, [ 'versionTime' => $this->versionTime, ]); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $operation); } public function testCreateCopy() { - $this->connection->copyBackup(Argument::allOf( - Argument::withEntry('instance', InstanceAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE)), - Argument::withEntry('backupId', self::COPIED_BACKUP), - Argument::withKey('sourceBackupId'), - Argument::withEntry('expireTime', $this->expireTime->format('Y-m-d\TH:i:s.u\Z')) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - - $this->backup->___setProperty('connection', $this->connection->reveal()); - $op = $this->backup->createCopy($this->copiedBackup, $this->expireTime); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $expected = [ + 'parent' => DatabaseAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE), + 'source_backup' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::INSTANCE, self::BACKUP), + 'expire_time' => $this->expireTime->format('U'), + ]; + $this->databaseAdminClient->copyBackup( + Argument::that(function (CopyBackupRequest $request) use ($expected) { + $this->assertEquals($expected['parent'], $request->getParent()); + $this->assertEquals(self::COPIED_BACKUP, $request->getBackupId()); + $this->assertEquals($expected['source_backup'], $request->getSourceBackup()); + $this->assertEquals($expected['expire_time'], $request->getExpireTime()->getSeconds()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); + + $copiedBackup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::COPIED_BACKUP + ); + + $op = $backup->createCopy($copiedBackup, $this->expireTime); + $this->assertInstanceOf(OperationResponse::class, $op); } public function testDelete() { - $this->connection->deleteBackup(Argument::withEntry('name', $this->backup->name())) - ->shouldBeCalled(); - - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->deleteBackup( + Argument::type(DeleteBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce(); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); - $this->backup->delete(); + $backup->delete(); } public function testInfo() { - $res = [ - 'name' => $this->backup->name(), - 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z'), - 'createTime' => $this->createTime->format('Y-m-d\TH:i:s.u\Z'), - 'versionTime' => $this->versionTime->format('Y-m-d\TH:i:s.u\Z') - ]; - - $this->connection->getBackup(Argument::withEntry('name', $this->backup->name())) - ->shouldBeCalledTimes(1) - ->willReturn($res); + $response = new BackupProto([ + 'name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::INSTANCE, self::BACKUP), + 'expire_time' => new Timestamp(['seconds' => $this->expireTime->format('U')]), + 'create_time' => new Timestamp(['seconds' => $this->expireTime->format('U')]), + 'version_time' => new Timestamp(['seconds' => $this->versionTime->format('U')]), + ]); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($response); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); - $info = $this->backup->info(); + $info = $backup->info(); - $this->assertEquals($res, $info); + $this->assertArraySubset([ + 'name' => $response->getName(), + 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.000000\Z'), + 'createTime' => $this->expireTime->format('Y-m-d\TH:i:s.000000\Z'), + 'versionTime' => $this->versionTime->format('Y-m-d\TH:i:s.000000\Z'), + ], $info); // Make sure the request only is sent once. - $this->backup->info(); + $backup->info(); } public function testReload() { - $res = [ - 'name' => $this->backup->name(), - 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z'), - 'createTime' => $this->createTime->format('Y-m-d\TH:i:s.u\Z'), - 'versionTime' => $this->versionTime->format('Y-m-d\TH:i:s.u\Z') - ]; - - $this->connection->getBackup(Argument::withEntry('name', $this->backup->name())) - ->shouldBeCalled() - ->willReturn($res); + $response = new BackupProto([ + 'name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::INSTANCE, self::BACKUP), + ]); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($response); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP, + ['name' => 'different-name'] + ); - $info = $this->backup->reload(); + $info = $backup->reload(); - $this->assertEquals($res, $info); + $this->assertArraySubset([ + 'name' => $response->getName(), + ], $info); } public function testState() { - $res = [ - 'state' => Backup::STATE_READY - ]; - $this->connection->getBackup(Argument::withEntry('name', $this->backup->name())) - ->shouldBeCalledTimes(1) - ->willReturn($res); + $response = new BackupProto([ + 'state' => Backup::STATE_READY, + ]); - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($response); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); - $this->assertEquals(Backup::STATE_READY, $this->backup->state()); + $this->assertEquals(Backup::STATE_READY, $backup->state()); // Make sure the request only is sent once. - $this->backup->state(); + $backup->state(); } public function testExists() { - $this->connection->getBackup(Argument::withEntry('name', $this->backup->name())) - ->shouldBeCalled() - ->willReturn([]); - - $this->backup->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new BackupProto()); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); - $this->assertTrue($this->backup->exists()); + $this->assertTrue($backup->exists()); } public function testUpdateExpireTime() { - $res = ['name' => 'foo', 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z')]; - - $this->connection->updateBackup(Argument::allOf( - Argument::withEntry('backup', [ - 'name' => $this->backup->name(), - 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z') - ]), - Argument::withEntry('updateMask', ['paths' => ['expire_time']]) - )) - ->shouldBeCalled() - ->willReturn($res); - - $this->backup->___setProperty('connection', $this->connection->reveal()); - - $info = $this->backup->updateExpireTime($this->expireTime); - $this->assertEquals($res, $info); + $newExpireTime = new DateTime("+1 day"); + + $response = new BackupProto([ + 'name' => 'foo', + 'expire_time' => new Timestamp(['seconds' => $newExpireTime->format('U')]), + ]); + + $this->databaseAdminClient->updateBackup( + Argument::that(function (UpdateBackupRequest $request) use ($newExpireTime) { + $this->assertEquals(new FieldMask(['paths' => ['expire_time']]), $request->getUpdateMask()); + $this->assertEquals($newExpireTime->format('U'), $request->getBackup()->getExpireTime()->getSeconds()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($response); + + $backup = new Backup( + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance->reveal(), + self::PROJECT_ID, + self::BACKUP + ); + + $info = $backup->updateExpireTime($newExpireTime); + $this->assertArraySubset([ + 'expireTime' => $newExpireTime->format('Y-m-d\TH:i:s.000000\Z'), + ], $info); } } diff --git a/Spanner/tests/Unit/Batch/BatchClientTest.php b/Spanner/tests/Unit/Batch/BatchClientTest.php index 3cb3b0b262f6..b95d35cf4901 100644 --- a/Spanner/tests/Unit/Batch/BatchClientTest.php +++ b/Spanner/tests/Unit/Batch/BatchClientTest.php @@ -17,22 +17,27 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\Timestamp; use Google\Cloud\Core\TimeTrait; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; -use Google\Cloud\Spanner\SpannerClient; +use Google\Cloud\Spanner\V1\BeginTransactionRequest; +use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\Cloud\Spanner\V1\CreateSessionRequest; +use Google\Cloud\Spanner\V1\Session; +use Google\Cloud\Spanner\V1\Transaction; +use Google\Protobuf\Timestamp as TimestampProto; /** * @group spanner @@ -41,56 +46,73 @@ */ class BatchClientTest extends TestCase { - use OperationRefreshTrait; use ProphecyTrait; - use StubCreationTrait; use TimeTrait; + use ApiHelperTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; - private $connection; - private $client; + private $spannerClient; + private $serializer; + private $batchClient; + private $spannerClient; public function setUp(): void { - $this->connection = $this->getConnStub(); - $this->client = TestHelpers::stub(BatchClient::class, [ - new Operation($this->connection->reveal(), false), - self::DATABASE - ], [ - 'operation' + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } ]); + $this->spannerClient = $this->prophesize(GapicSpannerClient::class); + $this->batchClient = new BatchClient( + new Operation($this->spannerClient->reveal(), $this->serializer, false), + self::DATABASE + ); } public function testSnapshot() { $time = time(); + $this->spannerClient->createSession( + Argument::that(function (CreateSessionRequest $request) { + $this->assertEquals( + $request->getDatabase(), + self::DATABASE + ); + return true; + }), + Argument::type('array') + )->shouldBeCalledOnce()->willReturn(new Session(['name' => self::SESSION])); + + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals( + $this->serializer->encodeMessage($request)['options']['readOnly'], + ['returnReadTimestamp' => true] + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Transaction([ + 'id' => self::TRANSACTION, + 'read_timestamp' => new TimestampProto(['seconds' => $time]) + ])); - $this->connection->createSession(Argument::withEntry('database', self::DATABASE)) - ->shouldBeCalledTimes(1) - ->willReturn([ - 'name' => self::SESSION - ]); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('session', self::SESSION), - Argument::that(function (array $args) { - if ($args['transactionOptions']['readOnly']['returnReadTimestamp'] !== true) { - return false; - } - - return $args['database'] === self::DATABASE; - }) - ))->shouldBeCalled()->willReturn([ - 'id' => self::TRANSACTION, - 'readTimestamp' => \DateTime::createFromFormat('U', (string) $time)->format(Timestamp::FORMAT) - ]); - - $this->refreshOperation($this->client, $this->connection->reveal()); - - $snapshot = $this->client->snapshot(); + $snapshot = $this->batchClient->snapshot(); $this->assertInstanceOf(BatchSnapshot::class, $snapshot); } @@ -104,7 +126,7 @@ public function testSnapshotFromString() 'readTimestamp' => \DateTime::createFromFormat('U', (string) $time)->format(Timestamp::FORMAT) ])); - $snapshot = $this->client->snapshotFromString($identifier); + $snapshot = $this->batchClient->snapshotFromString($identifier); $this->assertEquals(self::SESSION, $snapshot->session()->name()); $this->assertEquals(self::TRANSACTION, $snapshot->id()); $this->assertEquals( @@ -122,7 +144,7 @@ public function testQueryPartitionFromString() $partition = new QueryPartition($token, $sql, $options); $string = (string) $partition; - $res = $this->client->partitionFromString($partition); + $res = $this->batchClient->partitionFromString($partition); $this->assertEquals($token, $res->token()); $this->assertEquals($sql, $res->sql()); $this->assertEquals($options, $res->options()); @@ -139,7 +161,7 @@ public function testReadPartitionFromString() $partition = new ReadPartition($token, $table, $keyset, $columns, $options); $string = (string) $partition; - $res = $this->client->partitionFromString($partition); + $res = $this->batchClient->partitionFromString($partition); $this->assertEquals($token, $res->token()); $this->assertEquals($table, $res->table()); $this->assertEquals($keyset->keySetObject(), $res->keySet()->keySetObject()); @@ -153,7 +175,7 @@ public function testMissingPartitionTypeKey() $this->expectExceptionMessage('Invalid partition data.'); $data = base64_encode(json_encode(['hello' => 'world'])); - $this->client->partitionFromString($data); + $this->batchClient->partitionFromString($data); } public function testInvalidPartitionType() @@ -162,36 +184,43 @@ public function testInvalidPartitionType() $this->expectExceptionMessage('Invalid partition type.'); $data = base64_encode(json_encode([BatchClient::PARTITION_TYPE_KEY => uniqid('this-is-not-real')])); - $this->client->partitionFromString($data); + $this->batchClient->partitionFromString($data); } public function testSnapshotDatabaseRole() { $time = time(); + $this->spannerClient->createSession( + Argument::that(function (CreateSessionRequest $request) { + return $this->serializer->encodeMessage($request)['session']['creatorRole'] == 'Reader'; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Session(['name' => self::SESSION])); + + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals( + $this->serializer->encodeMessage($request)['options']['readOnly'], + ['returnReadTimestamp' => true] + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Transaction([ + 'id' => self::TRANSACTION, + 'read_timestamp' => new TimestampProto(['seconds' => $time]) + ])); - $client = TestHelpers::stub(BatchClient::class, [ - new Operation($this->connection->reveal(), false), + $batchClient = new BatchClient( + new Operation($this->spannerClient->reveal(), $this->serializer, false), self::DATABASE, ['databaseRole' => 'Reader'] - ], [ - 'operation' - ]); + ); - $this->connection->beginTransaction(Argument::any()) - ->shouldBeCalled()->willReturn([ - 'id' => self::TRANSACTION, - 'readTimestamp' => \DateTime::createFromFormat('U', (string) $time)->format(Timestamp::FORMAT) - ]); - - $this->connection->createSession(Argument::withEntry( - 'session', - ['labels' => [], 'creator_role' => 'Reader'] - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => self::SESSION - ]); - - $snapshot = $client->snapshot(); + $snapshot = $batchClient->snapshot(); } } diff --git a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php index 84717d6f873f..a0f3a05453c9 100644 --- a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php @@ -17,7 +17,9 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\PartitionInterface; use Google\Cloud\Spanner\Batch\QueryPartition; @@ -26,11 +28,15 @@ use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\V1\SpannerClient; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; +use Google\Cloud\Spanner\V1\PartitionReadRequest; +use Google\Cloud\Spanner\V1\PartitionQueryRequest; +use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\Partition; +use Google\Cloud\Spanner\V1\PartitionResponse; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -42,19 +48,20 @@ */ class BatchSnapshotTest extends TestCase { - use OperationRefreshTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; + use ApiHelperTrait; const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const SESSION = 'projects/my-awesome-project/instances/my-instance/databases/my-database/sessions/session-id'; const TRANSACTION = 'transaction-id'; + private $spannerClient; + private $serializer; private $session; private $timestamp; - private $connection; private $snapshot; + private $spannerClient; public function setUp(): void { @@ -68,29 +75,43 @@ public function setUp(): void $this->timestamp = new Timestamp(new \DateTime()); - $this->connection = $this->getConnStub(); - $this->snapshot = TestHelpers::stub(BatchSnapshot::class, [ - new Operation($this->connection->reveal(), false), + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]); + $this->spannerClient = $this->prophesize(SpannerClient::class); + + $this->snapshot = new BatchSnapshot( + new Operation($this->spannerClient->reveal(), $this->serializer, false), $this->session->reveal(), ['id' => self::TRANSACTION, 'readTimestamp' => $this->timestamp] - ], [ - 'operation', 'session' - ]); + ); } public function testClose() { $session = $this->prophesize(Session::class); - $session->delete([])->shouldBeCalled(); + $session->delete([])->shouldBeCalledOnce(); + + $this->snapshot = new BatchSnapshot( + $this->prophesize(Operation::class)->reveal(), + $session->reveal() + ); - $this->snapshot->___setProperty('session', $session->reveal()); $this->snapshot->close(); } - /** - * @dataProvider partitionReadAndQueryOptions - */ - public function testPartitionRead($testCaseOptions) + public function testPartitionRead() { $table = 'table'; $keySet = new KeySet(['all' => true]); @@ -99,15 +120,14 @@ public function testPartitionRead($testCaseOptions) 'index' => 'foo', 'maxPartitions' => 10, 'partitionSizeBytes' => 1 - ] + $testCaseOptions; + ]; $expectedArguments = [ 'session' => self::SESSION, - 'database' => self::DATABASE, - 'transactionId' => self::TRANSACTION, + 'transaction' => ['id' => self::TRANSACTION], 'table' => $table, 'columns' => $columns, - 'keySet' => $keySet->keySetObject(), + 'keySet' => $keySet->keySetObject() + ['keys' => [], 'ranges' => []], 'index' => $opts['index'], 'partitionOptions' => [ 'maxPartitions' => $opts['maxPartitions'], @@ -115,23 +135,18 @@ public function testPartitionRead($testCaseOptions) ] ]; - $expectedArguments += $testCaseOptions; - - $this->connection->partitionRead(Argument::that( - function ($actualArguments) use ($expectedArguments) { + $this->spannerClient->partitionRead( + Argument::that(function (PartitionReadRequest $request) use ($expectedArguments) { + $actualArguments = $this->serializer->encodeMessage($request); return $actualArguments == $expectedArguments; - } - ))->shouldBeCalled()->willReturn([ + }), + Argument::type('array') + )->shouldBeCalledOnce()->willReturn(new PartitionResponse([ 'partitions' => [ - [ - 'partitionToken' => 'token1' - ], [ - 'partitionToken' => 'token2' - ] + new Partition(['partition_token' => 'token1']), + new Partition(['partition_token' => 'token2']) ] - ]); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + ])); $partitions = $this->snapshot->partitionRead($table, $keySet, $columns, $opts); $this->assertContainsOnlyInstancesOf(ReadPartition::class, $partitions); @@ -142,10 +157,7 @@ function ($actualArguments) use ($expectedArguments) { $this->assertEquals($opts, $partitions[0]->options()); } - /** - * @dataProvider partitionReadAndQueryOptions - */ - public function testPartitionQuery(array $testCaseOptions) + public function testPartitionQuery() { $sql = 'SELECT 1=1'; $opts = [ @@ -154,38 +166,32 @@ public function testPartitionQuery(array $testCaseOptions) ], 'maxPartitions' => 10, 'partitionSizeBytes' => 1 - ] + $testCaseOptions; + ]; $expectedArguments = [ 'session' => self::SESSION, - 'database' => self::DATABASE, - 'transactionId' => self::TRANSACTION, + 'transaction' => ['id' => self::TRANSACTION], 'sql' => $sql, 'params' => $opts['parameters'], - 'paramTypes' => ['foo' => ['code' => 6]], + 'paramTypes' => ['foo' => ['code' => 6, 'typeAnnotation' => 0, 'protoTypeFqn' => '']], 'partitionOptions' => [ 'maxPartitions' => $opts['maxPartitions'], 'partitionSizeBytes' => $opts['partitionSizeBytes'] ] ]; - $expectedArguments += $testCaseOptions; - - $this->connection->partitionQuery(Argument::that( - function ($actualArguments) use ($expectedArguments) { + $this->spannerClient->partitionQuery( + Argument::that(function (PartitionQueryRequest $request) use ($expectedArguments) { + $actualArguments = $this->serializer->encodeMessage($request); return $actualArguments == $expectedArguments; - } - ))->shouldBeCalled()->willReturn([ - 'partitions' => [ - [ - 'partitionToken' => 'token1' - ], [ - 'partitionToken' => 'token2' + }), + Argument::type('array') + )->shouldBeCalledOnce()->willReturn(new PartitionResponse([ + 'partitions' => [ + new Partition(['partition_token' => 'token1']), + new Partition(['partition_token' => 'token2']) ] - ] - ]); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + ])); $partitions = $this->snapshot->partitionQuery($sql, $opts); $this->assertContainsOnlyInstancesOf(QueryPartition::class, $partitions); @@ -209,17 +215,25 @@ public function testExecuteQueryPartition() $partition = new QueryPartition($token, $sql, $opts); - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('partitionToken', $token), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('sql', $sql), - Argument::withEntry('params', $opts['parameters']), - Argument::withEntry('paramTypes', ['foo' => ['code' => 6]]) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql, $opts, $token) { + $this->assertEquals($request->getSql(), $sql); + $this->assertEquals($request->getSession(), self::SESSION); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getPartitionToken(), $token); + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['params'], $opts['parameters']); + $this->assertEquals( + $message['paramTypes'], + ['foo' => ['code' => 6, 'typeAnnotation' => 0, 'protoTypeFqn' => '']] + ); + return true; + }), + Argument::type('array') + )->shouldBeCalledOnce()->willReturn( + $this->resultGeneratorStream() + ); + $res = $this->snapshot->executePartition($partition); $this->assertInstanceOf(Result::class, $res); $rows = iterator_to_array($res->rows()); @@ -238,18 +252,25 @@ public function testExecuteReadPartition() $partition = new ReadPartition($token, $table, $keySet, $columns, $opts); - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('partitionToken', $token), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('table', $table), - Argument::withEntry('columns', $columns), - Argument::withEntry('keySet', $keySet->keySetObject()), - Argument::withEntry('index', $opts['index']) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->snapshot, $this->connection->reveal()); + $this->spannerClient->streamingRead( + Argument::that(function (ReadRequest $request) use ($token, $table, $columns, $keySet, $opts) { + $this->assertEquals($request->getSession(), self::SESSION); + $this->assertEquals($request->getPartitionToken(), $token); + $this->assertEquals($request->getTable(), $table); + $this->assertEquals($request->getIndex(), $opts['index']); + $this->assertEquals(iterator_to_array($request->getColumns()), $columns); + $this->assertEquals( + $request->getTransaction()->getId(), + self::TRANSACTION + ); + $this->assertTrue($this->serializer->encodeMessage($request->getKeySet())['all']); + return true; + }), + Argument::type('array') + )->shouldBeCalledOnce()->willReturn( + $this->resultGeneratorStream() + ); + $res = $this->snapshot->executePartition($partition); $this->assertInstanceOf(Result::class, $res); $rows = iterator_to_array($res->rows()); @@ -276,14 +297,6 @@ public function testExecutePartitionInvalidType() $dummy = new DummyPartition; $this->snapshot->executePartition($dummy); } - - public function partitionReadAndQueryOptions() - { - return [ - [['dataBoostEnabled' => false]], - [['dataBoostEnabled' => true]] - ]; - } } //@codingStandardsIgnoreStart diff --git a/Spanner/tests/Unit/Connection/GrpcTest.php b/Spanner/tests/Unit/Connection/GrpcTest.php deleted file mode 100644 index 4b42ecc0d1d3..000000000000 --- a/Spanner/tests/Unit/Connection/GrpcTest.php +++ /dev/null @@ -1,1690 +0,0 @@ -checkAndSkipGrpcTests(); - - $this->requestWrapper = $this->prophesize(GrpcRequestWrapper::class); - $this->serializer = new Serializer; - $this->successMessage = 'success'; - $this->lro = $this->prophesize(OperationResponse::class)->reveal(); - } - - public function testApiEndpoint() - { - $expected = 'foobar.com'; - - $grpc = new GrpcStub(['apiEndpoint' => $expected]); - - $this->assertEquals($expected, $grpc->config['apiEndpoint']); - } - - public function testListInstanceConfigs() - { - $this->assertCallCorrect('listInstanceConfigs', [ - 'projectName' => self::PROJECT - ], $this->expectResourceHeader(self::PROJECT, [ - self::PROJECT - ])); - } - - public function testGetInstanceConfig() - { - $this->assertCallCorrect('getInstanceConfig', [ - 'name' => self::CONFIG, - 'projectName' => self::PROJECT - ], $this->expectResourceHeader(self::PROJECT, [ - self::CONFIG - ])); - } - - public function testCreateInstanceConfig() - { - list ($args, $config) = $this->instanceConfig(); - - $this->assertCallCorrect( - 'createInstanceConfig', - [ - 'projectName' => self::PROJECT, - 'instanceConfigId' => self::CONFIG - ] + $args, - $this->expectResourceHeader(self::CONFIG, [ - self::PROJECT, - self::CONFIG, - $config - ]), - $this->lro, - null - ); - } - - public function testUpdateInstanceConfig() - { - list ($args, $config, $fieldMask) = $this->instanceConfig(false); - $this->assertCallCorrect('updateInstanceConfig', $args, $this->expectResourceHeader(self::CONFIG, [ - $config, $fieldMask - ]), $this->lro, null); - } - - public function testDeleteInstanceConfig() - { - $this->assertCallCorrect('deleteInstanceConfig', [ - 'name' => self::CONFIG - ], $this->expectResourceHeader(self::CONFIG, [ - self::CONFIG - ])); - } - - public function testListInstances() - { - $this->assertCallCorrect('listInstances', [ - 'projectName' => self::PROJECT - ], $this->expectResourceHeader(self::PROJECT, [ - self::PROJECT - ])); - } - - public function testGetInstance() - { - $this->assertCallCorrect('getInstance', [ - 'name' => self::INSTANCE, - 'projectName' => self::PROJECT - ], $this->expectResourceHeader(self::PROJECT, [ - self::INSTANCE - ])); - } - - public function testGetInstanceWithFieldMaskArray() - { - $fieldNames = ['name', 'displayName', 'nodeCount']; - - $mask = []; - foreach (array_values($fieldNames) as $key) { - $mask[] = Serializer::toSnakeCase($key); - } - - $fieldMask = $this->serializer->decodeMessage(new FieldMask, ['paths' => $mask]); - $this->assertCallCorrect('getInstance', [ - 'name' => self::INSTANCE, - 'projectName' => self::PROJECT, - 'fieldMask' => $fieldNames - ], $this->expectResourceHeader(self::PROJECT, [ - self::INSTANCE, - ['fieldMask' => $fieldMask] - ])); - } - - public function testGetInstanceWithFieldMaskString() - { - $fieldNames = 'nodeCount'; - $mask[] = Serializer::toSnakeCase($fieldNames); - - $fieldMask = $this->serializer->decodeMessage(new FieldMask, ['paths' => $mask]); - $this->assertCallCorrect('getInstance', [ - 'name' => self::INSTANCE, - 'projectName' => self::PROJECT, - 'fieldMask' => $fieldNames - ], $this->expectResourceHeader(self::PROJECT, [ - self::INSTANCE, - ['fieldMask' => $fieldMask] - ])); - } - - public function testCreateInstance() - { - list ($args, $instance) = $this->instance(); - - $this->assertCallCorrect('createInstance', [ - 'projectName' => self::PROJECT, - 'instanceId' => self::INSTANCE - ] + $args, $this->expectResourceHeader(self::INSTANCE, [ - self::PROJECT, - self::INSTANCE, - $instance - ]), $this->lro, null); - } - - public function testCreateInstanceWithProcessingNodes() - { - list ($args, $instance) = $this->instance(true, false); - - $this->assertCallCorrect('createInstance', [ - 'projectName' => self::PROJECT, - 'instanceId' => self::INSTANCE, - 'processingUnits' => 1000 - ] + $args, $this->expectResourceHeader(self::INSTANCE, [ - self::PROJECT, - self::INSTANCE, - $instance - ]), $this->lro, null); - } - - public function testUpdateInstance() - { - list ($args, $instance, $fieldMask) = $this->instance(false); - - $this->assertCallCorrect('updateInstance', $args, $this->expectResourceHeader(self::INSTANCE, [ - $instance, $fieldMask - ]), $this->lro, null); - } - - public function testDeleteInstance() - { - $this->assertCallCorrect('deleteInstance', [ - 'name' => self::INSTANCE - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE - ])); - } - - public function testSetInstanceIamPolicy() - { - $policy = ['foo' => 'bar']; - - $this->assertCallCorrect('setInstanceIamPolicy', [ - 'resource' => self::INSTANCE, - 'policy' => $policy - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE, - $policy - ], false)); - } - - public function testGetInstanceIamPolicy() - { - $this->assertCallCorrect('getInstanceIamPolicy', [ - 'resource' => self::INSTANCE - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE - ])); - } - - public function testTestInstanceIamPermissions() - { - $permissions = ['permission1', 'permission2']; - $this->assertCallCorrect('testInstanceIamPermissions', [ - 'resource' => self::INSTANCE, - 'permissions' => $permissions - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE, - $permissions - ], false)); - } - - public function testListDatabases() - { - $this->assertCallCorrect('listDatabases', [ - 'instance' => self::INSTANCE - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE - ])); - } - - public function testCreateDatabase() - { - $createStmt = 'CREATE Foo'; - $extraStmts = [ - 'CREATE TABLE Bar' - ]; - $encryptionConfig = ['kmsKeyName' => 'kmsKeyName']; - $expectedEncryptionConfig = $this->serializer->decodeMessage(new EncryptionConfig, $encryptionConfig); - - $this->assertCallCorrect('createDatabase', [ - 'instance' => self::INSTANCE, - 'createStatement' => $createStmt, - 'extraStatements' => $extraStmts, - 'encryptionConfig' => $encryptionConfig - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE, - $createStmt, - [ - 'extraStatements' => $extraStmts, - 'encryptionConfig' => $expectedEncryptionConfig - ] - ]), $this->lro, null); - } - - public function testCreateBackup() - { - $backupId = "backup-id"; - $expireTime = new \DateTime("+ 7 hours"); - $backup = [ - 'database' => self::DATABASE, - 'expireTime' => $expireTime->format('Y-m-d\TH:i:s.u\Z') - ]; - $expectedBackup = $this->serializer->decodeMessage(new Backup(), [ - 'expireTime' => $this->formatTimestampForApi($backup['expireTime']) - ] + $backup); - - $encryptionConfig = [ - 'kmsKeyName' => 'kmsKeyName', - 'encryptionType' => CreateBackupEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION - ]; - $expectedEncryptionConfig = $this->serializer->decodeMessage( - new CreateBackupEncryptionConfig, - $encryptionConfig - ); - - $this->assertCallCorrect('createBackup', [ - 'instance' => self::INSTANCE, - 'backupId' => $backupId, - 'backup' => $backup, - 'encryptionConfig' => $encryptionConfig - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE, - $backupId, - $expectedBackup, - [ - 'encryptionConfig' => $expectedEncryptionConfig - ] - ]), $this->lro, null); - } - - public function testRestoreDatabase() - { - $databaseId = 'test-database'; - $encryptionConfig = [ - 'kmsKeyName' => 'kmsKeyName', - 'encryptionType' => RestoreDatabaseEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION - ]; - $expectedEncryptionConfig = $this->serializer->decodeMessage( - new RestoreDatabaseEncryptionConfig, - $encryptionConfig - ); - - $this->assertCallCorrect('restoreDatabase', [ - 'instance' => self::INSTANCE, - 'databaseId' => $databaseId, - 'encryptionConfig' => $encryptionConfig - ], $this->expectResourceHeader(self::INSTANCE, [ - self::INSTANCE, - $databaseId, - [ - 'encryptionConfig' => $expectedEncryptionConfig - ] - ]), $this->lro, null); - } - - public function testUpdateDatabaseDdl() - { - $statements = [ - 'CREATE TABLE Bar' - ]; - - $this->assertCallCorrect('updateDatabaseDdl', [ - 'name' => self::DATABASE, - 'statements' => $statements - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE, - $statements - ], false), $this->lro, null); - } - - public function testDropDatabase() - { - $this->assertCallCorrect('dropDatabase', [ - 'name' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE - ])); - } - - public function testGetDatabase() - { - $this->assertCallCorrect('getDatabase', [ - 'name' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE - ])); - } - - public function testGetDatabaseDdl() - { - $this->assertCallCorrect('getDatabaseDdl', [ - 'name' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE - ])); - } - - public function testSetDatabaseIamPolicy() - { - $policy = ['foo' => 'bar']; - - $this->assertCallCorrect('setDatabaseIamPolicy', [ - 'resource' => self::DATABASE, - 'policy' => $policy - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE, - $policy - ], false)); - } - - public function testGetDatabaseIamPolicy() - { - $this->assertCallCorrect('getDatabaseIamPolicy', [ - 'resource' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE - ])); - } - - public function testTestDatabaseIamPermissions() - { - $permissions = ['permission1', 'permission2']; - $this->assertCallCorrect('testDatabaseIamPermissions', [ - 'resource' => self::DATABASE, - 'permissions' => $permissions - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE, - $permissions - ], false)); - } - - /** - * @dataProvider larOptions - */ - public function testCreateSession($larEnabled, $grpcConfig) - { - $labels = ['foo' => 'bar']; - - $this->assertCallCorrect('createSession', [ - 'database' => self::DATABASE, - 'session' => [ - 'labels' => $labels - ] - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE, - [ - 'session' => (new Session)->setLabels($labels) - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testCreateSessionAsync() - { - $promise = $this->prophesize(PromiseInterface::class)->reveal(); - $client = $this->prophesize(SpannerClient::class); - $transport = $this->prophesize(TransportInterface::class); - $transport->startUnaryCall( - Argument::type(Call::class), - Argument::withEntry('headers', [ - 'x-goog-spanner-route-to-leader' => ['true'], - 'google-cloud-resource-prefix' => ['database1'] - ]) - )->willReturn($promise); - - $client->getTransport()->willReturn($transport->reveal()); - - $grpc = new Grpc(['gapicSpannerClient' => $client->reveal()]); - - $promise = $grpc->createSessionAsync([ - 'database' => 'database1', - 'session' => [ - 'labels' => [ 'foo' => 'bar' ] - ] - ]); - - $this->assertInstanceOf(PromiseInterface::class, $promise); - } - - /** - * @dataProvider larOptions - */ - public function testBatchCreateSessions($larEnabled, $grpcConfig) - { - $count = 10; - $template = [ - 'labels' => [ - 'foo' => 'bar' - ] - ]; - - $this->assertCallCorrect('batchCreateSessions', [ - 'database' => self::DATABASE, - 'sessionCount' => $count, - 'sessionTemplate' => $template - ], $this->expectResourceHeader(self::DATABASE, [ - self::DATABASE, $count, [ - 'sessionTemplate' => $this->serializer->decodeMessage(new Session, $template) - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testBatchWrite() - { - $mutationGroups = [ - (new MutationGroup(false)) - ->insertOrUpdate( - "Singers", - ['SingerId' => 16, 'FirstName' => 'Scarlet', 'LastName' => 'Terry'] - )->toArray(), - (new MutationGroup(false)) - ->insertOrUpdate( - "Singers", - ['SingerId' => 17, 'FirstName' => 'Marc', 'LastName' => 'Kristen'] - )->insertOrUpdate( - "Albums", - ['AlbumId' => 1, 'SingerId' => 17, 'AlbumTitle' => 'Total Junk'] - )->toArray() - ]; - - $expectedMutationGroups = [ - new MutationGroupProto(['mutations' => [ - new Mutation(['insert_or_update' => new Write([ - 'table' => 'Singers', - 'columns' => ['SingerId', 'FirstName', 'LastName'], - 'values' => [new ListValue(['values' => [ - new Value(['string_value' => '16']), - new Value(['string_value' => 'Scarlet']), - new Value(['string_value' => 'Terry']) - ]])] - ])]) - ]]), - new MutationGroupProto(['mutations' => [ - new Mutation(['insert_or_update' => new Write([ - 'table' => 'Singers', - 'columns' => ['SingerId', 'FirstName', 'LastName'], - 'values' => [new ListValue(['values' => [ - new Value(['string_value' => '17']), - new Value(['string_value' => 'Marc']), - new Value(['string_value' => 'Kristen']) - ]])] - ])]), - new Mutation(['insert_or_update' => new Write([ - 'table' => 'Albums', - 'columns' => ['AlbumId', 'SingerId', 'AlbumTitle'], - 'values' => [new ListValue(['values' => [ - new Value(['string_value' => '1']), - new Value(['string_value' => '17']), - new Value(['string_value' => 'Total Junk']) - ]])] - ])]), - ]]), - ]; - - $this->assertCallCorrect( - 'batchWrite', - [ - 'database' => self::DATABASE, - 'session' => self::SESSION, - 'mutationGroups' => $mutationGroups, - ], - $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $expectedMutationGroups, - [] - ]), - ); - } - - /** - * @dataProvider larOptions - */ - public function testGetSession($larEnabled, $grpcConfig) - { - $this->assertCallCorrect('getSession', [ - 'database' => self::DATABASE, - 'name' => self::SESSION - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testDeleteSession() - { - $this->assertCallCorrect('deleteSession', [ - 'database' => self::DATABASE, - 'name' => self::SESSION - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION - ])); - } - - public function testDeleteSessionAsync() - { - $promise = $this->prophesize(PromiseInterface::class) - ->reveal(); - $sessionName = 'session1'; - $databaseName = 'database1'; - $request = new DeleteSessionRequest(); - $request->setName($sessionName); - $client = $this->prophesize(SpannerClient::class); - $transport = $this->prophesize(TransportInterface::class); - $transport->startUnaryCall( - Argument::type(Call::class), - Argument::type('array') - )->willReturn($promise); - $client->getTransport() - ->willReturn($transport->reveal()); - $grpc = new Grpc(['gapicSpannerClient' => $client->reveal()]); - $call = $grpc->deleteSessionAsync([ - 'name' => $sessionName, - 'database' => $databaseName - ]); - - $this->assertInstanceOf(PromiseInterface::class, $call); - } - - /** - * @dataProvider larOptions - */ - public function testExecuteStreamingSql($larEnabled, $grpcConfig) - { - $sql = 'SELECT 1'; - - $mapper = new ValueMapper(false); - $mapped = $mapper->formatParamsForExecuteSql(['foo' => 'bar']); - - $expectedParams = $this->serializer->decodeMessage( - new Struct, - $this->formatStructForApi($mapped['params']) - ); - - $expectedParamTypes = $mapped['paramTypes']; - foreach ($expectedParamTypes as $key => $param) { - $expectedParamTypes[$key] = $this->serializer->decodeMessage(new Type, $param); - } - - $this->assertCallCorrect('executeStreamingSql', [ - 'session' => self::SESSION, - 'sql' => $sql, - 'transactionId' => self::TRANSACTION, - 'database' => self::DATABASE, - 'headers' => ['x-goog-spanner-route-to-leader' => ['true']] - ] + $mapped, $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $sql, - [ - 'transaction' => $this->transactionSelector(), - 'params' => $expectedParams, - 'paramTypes' => $expectedParamTypes - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testExecuteStreamingSqlWithRequestOptions() - { - $sql = 'SELECT 1'; - $requestOptions = ["priority" => RequestOptions\Priority::PRIORITY_LOW]; - $expectedRequestOptions = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - - $this->assertCallCorrect('executeStreamingSql', [ - 'session' => self::SESSION, - 'sql' => $sql, - 'transactionId' => self::TRANSACTION, - 'database' => self::DATABASE, - 'params' => [], - 'requestOptions' => $requestOptions - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $sql, - [ - 'transaction' => $this->transactionSelector(), - 'requestOptions' => $expectedRequestOptions - ] - ])); - } - - /** - * @dataProvider queryOptions - */ - public function testExecuteStreamingSqlWithQueryOptions( - array $methodOptions, - array $envOptions, - array $clientOptions, - array $expectedOptions - ) { - $sql = 'SELECT 1'; - - if (array_key_exists('optimizerVersion', $envOptions)) { - putenv('SPANNER_OPTIMIZER_VERSION=' . $envOptions['optimizerVersion']); - } - if (array_key_exists('optimizerStatisticsPackage', $envOptions)) { - putenv('SPANNER_OPTIMIZER_STATISTICS_PACKAGE=' . $envOptions['optimizerStatisticsPackage']); - } - $gapic = $this->prophesize(SpannerClient::class); - $gapic->executeStreamingSql( - self::SESSION, - $sql, - Argument::that(function ($arguments) use ($expectedOptions) { - $queryOptions = $arguments['queryOptions'] ?? null; - $expectedOptions += ['optimizerVersion' => null, 'optimizerStatisticsPackage' => null]; - $this->assertEquals( - $queryOptions ? $queryOptions->getOptimizerVersion() : null, - $expectedOptions['optimizerVersion'] - ); - $this->assertEquals( - $queryOptions ? $queryOptions->getOptimizerStatisticsPackage() : null, - $expectedOptions['optimizerStatisticsPackage'] - ); - return true; - }) - )->shouldBeCalledOnce(); - - $grpc = new Grpc([ - 'gapicSpannerClient' => $gapic->reveal() - ] + ['queryOptions' => $clientOptions]); - - $grpc->executeStreamingSql([ - 'database' => self::DATABASE, - 'session' => self::SESSION, - 'sql' => $sql, - 'params' => [] - ] + ['queryOptions' => $methodOptions]); - - if ($envOptions) { - putenv('SPANNER_OPTIMIZER_VERSION='); - putenv('SPANNER_OPTIMIZER_STATISTICS_PACKAGE='); - } - } - - public function queryOptions() - { - return [ - [ - ['optimizerVersion' => '8'], - [ - 'optimizerVersion' => '7', - 'optimizerStatisticsPackage' => "auto_20191128_18_47_22UTC", - ], - ['optimizerStatisticsPackage' => "auto_20191128_14_47_22UTC"], - [ - 'optimizerVersion' => '8', - 'optimizerStatisticsPackage' => "auto_20191128_18_47_22UTC", - ] - ], - [ - [], - ['optimizerVersion' => '7'], - [ - 'optimizerVersion' => '6', - 'optimizerStatisticsPackage' => "auto_20191128_14_47_22UTC", - ], - [ - 'optimizerVersion' => '7', - 'optimizerStatisticsPackage' => "auto_20191128_14_47_22UTC", - ] - ], - [ - ['optimizerStatisticsPackage' => "auto_20191128_23_47_22UTC"], - [], - [ - 'optimizerVersion' => '6', - 'optimizerStatisticsPackage' => "auto_20191128_14_47_22UTC", - ], - [ - 'optimizerVersion' => '6', - 'optimizerStatisticsPackage' => "auto_20191128_23_47_22UTC", - ] - ], - [ - [], - [], - [], - [] - ] - ]; - } - - /** - * @dataProvider readKeysets - */ - public function testStreamingRead($keyArg, $keyObj, $larEnabled, $grpcConfig) - { - $columns = [ - 'id', - 'name' - ]; - - $this->assertCallCorrect('streamingRead', [ - 'keySet' => $keyArg, - 'transactionId' => self::TRANSACTION, - 'session' => self::SESSION, - 'table' => self::TABLE, - 'columns' => $columns, - 'database' => self::DATABASE, - 'headers' => ['x-goog-spanner-route-to-leader' => ['true']] - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - self::TABLE, - $columns, - $keyObj, - [ - 'transaction' => $this->transactionSelector() - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testStreamingReadWithRequestOptions() - { - $columns = [ - 'id', - 'name' - ]; - $requestOptions = ['priority' => RequestOptions\Priority::PRIORITY_LOW]; - $expectedRequestOptions = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - - $this->assertCallCorrect('streamingRead', [ - 'keySet' => [], - 'transactionId' => self::TRANSACTION, - 'session' => self::SESSION, - 'table' => self::TABLE, - 'columns' => $columns, - 'database' => self::DATABASE, - 'requestOptions' => $requestOptions - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - self::TABLE, - $columns, - new KeySet, - [ - 'transaction' => $this->transactionSelector(), - 'requestOptions' => $expectedRequestOptions - ] - ])); - } - - public function readKeysets() - { - $this->setUp(); - - return [ - [ - [], - new KeySet, - true, - ['routeToLeader' => true] - ], [ - ['keys' => [1]], - $this->serializer->decodeMessage(new KeySet, [ - 'keys' => [ - [ - 'values' => [ - [ - 'number_value' => 1 - ] - ] - ] - ] - ]), - false, - ['routeToLeader' => false] - ], [ - ['keys' => [[1,1]]], - $this->serializer->decodeMessage(new KeySet, [ - 'keys' => [ - [ - 'values' => [ - [ - 'number_value' => 1 - ], - [ - 'number_value' => 1 - ] - ] - ] - ] - ]), - false, - ['routeToLeader' => false] - ] - ]; - } - - /** - * @dataProvider larOptions - */ - public function testExecuteBatchDml($larEnabled, $grpcConfig) - { - $statements = [ - [ - 'sql' => 'SELECT 1', - 'params' => [] - ] - ]; - - $statementsObjs = [ - new Statement([ - 'sql' => 'SELECT 1' - ]) - ]; - - $this->assertCallCorrect('executeBatchDml', [ - 'session' => self::SESSION, - 'database' => self::DATABASE, - 'transactionId' => self::TRANSACTION, - 'statements' => $statements, - 'seqno' => 1 - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $this->transactionSelector(), - $statementsObjs, - 1 - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function testExecuteBatchDmlWithRequestOptions() - { - $statements = [ - [ - 'sql' => 'SELECT 1', - 'params' => [] - ] - ]; - - $statementsObjs = [ - new Statement([ - 'sql' => 'SELECT 1' - ]) - ]; - $requestOptions = ['priority' => RequestOptions\Priority::PRIORITY_LOW]; - $expectedRequestOptions = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - - - $this->assertCallCorrect('executeBatchDml', [ - 'session' => self::SESSION, - 'database' => self::DATABASE, - 'transactionId' => self::TRANSACTION, - 'statements' => $statements, - 'seqno' => 1, - 'requestOptions' => $requestOptions - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $this->transactionSelector(), - $statementsObjs, - 1, - ['requestOptions' => $expectedRequestOptions] - ], true, true)); - } - - /** - * @dataProvider transactionTypes - */ - public function testBeginTransaction($optionsArr, $optionsObj, $larEnabled, $grpcConfig) - { - $this->assertCallCorrect('beginTransaction', [ - 'session' => self::SESSION, - 'transactionOptions' => $optionsArr, - 'database' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $optionsObj - ], true, $larEnabled, $optionsArr), null, '', $grpcConfig); - } - - public function transactionTypes() - { - $ts = (new \DateTime)->format('Y-m-d\TH:i:s.u\Z'); - $pbTs = new Timestamp($this->formatTimestampForApi($ts)); - $readOnlyClass = PHP_VERSION_ID >= 80100 - ? PBReadOnly::class - : 'Google\Cloud\Spanner\V1\TransactionOptions\ReadOnly'; - - return [ - [ - ['readWrite' => []], - new TransactionOptions([ - 'read_write' => new ReadWrite - ]), - true, - ['routeToLeader' => true] - ], [ - [ - 'readOnly' => [ - 'minReadTimestamp' => $ts, - 'readTimestamp' => $ts - ] - ], - new TransactionOptions([ - 'read_only' => new $readOnlyClass([ - 'min_read_timestamp' => $pbTs, - 'read_timestamp' => $pbTs - ]) - ]), - true, - ['routeToLeader' => true] - ], [ - ['partitionedDml' => []], - new TransactionOptions([ - 'partitioned_dml' => new PartitionedDml - ]), - true, - ['routeToLeader' => true] - ] - ]; - } - - /** - * @dataProvider commit - */ - public function testCommit($mutationsArr, $mutationsObjArr, $larEnabled, $grpcConfig) - { - $this->assertCallCorrect('commit', [ - 'session' => self::SESSION, - 'mutations' => $mutationsArr, - 'singleUseTransaction' => true, - 'database' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $mutationsObjArr, - [ - 'singleUseTransaction' => new TransactionOptions([ - 'read_write' => new ReadWrite - ]) - ] - ], true, $grpcConfig), null, '', $grpcConfig); - } - - /** - * @dataProvider commit - */ - public function testCommitWithRequestOptions($mutationsArr, $mutationsObjArr) - { - $requestOptions = ['priority' => RequestOptions\Priority::PRIORITY_LOW]; - $expectedRequestOptions = $this->serializer->decodeMessage( - new RequestOptions, - $requestOptions - ); - $this->assertCallCorrect('commit', [ - 'session' => self::SESSION, - 'mutations' => $mutationsArr, - 'singleUseTransaction' => true, - 'database' => self::DATABASE, - 'requestOptions' => $requestOptions - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $mutationsObjArr, - [ - 'singleUseTransaction' => new TransactionOptions([ - 'read_write' => new ReadWrite - ]), - 'requestOptions' => $expectedRequestOptions - ] - ], true, true)); - } - - public function commit() - { - $mutation = [ - 'table' => self::TABLE, - 'columns' => [ - 'col1' - ], - 'values' => [ - 'val1' - ] - ]; - - $write = new Write([ - 'table' => self::TABLE, - 'columns' => ['col1'], - 'values' => [ - new ListValue([ - 'values' => [ - new Value([ - 'string_value' => 'val1' - ]) - ] - ]) - ] - ]); - - return [ - [ - [], [], true, ['routeToLeader' => true] - ], [ - [ - [ - 'delete' => [ - 'table' => self::TABLE, - 'keySet' => [] - ] - ] - ], - [ - new Mutation([ - 'delete' => new Delete([ - 'table' => self::TABLE, - 'key_set' => new KeySet - ]) - ]) - ], - true, - ['routeToLeader' => true] - ], [ - [ - [ - 'insert' => $mutation - ] - ], - [ - new Mutation([ - 'insert' => $write - ]) - ], - true, - ['routeToLeader' => true] - ], [ - [ - [ - 'update' => $mutation - ] - ], - [ - new Mutation([ - 'update' => $write - ]) - ], - true, - ['routeToLeader' => true] - ], [ - [ - [ - 'insertOrUpdate' => $mutation - ] - ], - [ - new Mutation([ - 'insert_or_update' => $write - ]) - ], - true, - ['routeToLeader' => true] - ], [ - [ - [ - 'replace' => $mutation - ] - ], - [ - new Mutation([ - 'replace' => $write - ]) - ], - true, - ['routeToLeader' => true] - ] - ]; - } - - /** - * @dataProvider larOptions - */ - public function testRollback($larEnabled, $grpcConfig) - { - $this->assertCallCorrect('rollback', [ - 'session' => self::SESSION, - 'transactionId' => self::TRANSACTION, - 'database' => self::DATABASE - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - self::TRANSACTION - ], true, $larEnabled), null, '', $grpcConfig); - } - - /** - * @dataProvider partitionOptions - */ - public function testPartitionQuery($partitionOptions, $partitionOptionsObj, $larEnabled, $grpcConfig) - { - $sql = 'SELECT 1'; - $this->assertCallCorrect('partitionQuery', [ - 'session' => self::SESSION, - 'sql' => $sql, - 'params' => [], - 'transactionId' => self::TRANSACTION, - 'database' => self::DATABASE, - 'partitionOptions' => $partitionOptions, - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - $sql, - [ - 'transaction' => $this->transactionSelector(), - 'partitionOptions' => $partitionOptionsObj - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - /** - * @dataProvider partitionOptions - */ - public function testPartitionRead($partitionOptions, $partitionOptionsObj, $larEnabled, $grpcConfig) - { - $this->assertCallCorrect('partitionRead', [ - 'session' => self::SESSION, - 'keySet' => [], - 'table' => self::TABLE, - 'transactionId' => self::TRANSACTION, - 'database' => self::DATABASE, - 'partitionOptions' => $partitionOptions, - ], $this->expectResourceHeader(self::DATABASE, [ - self::SESSION, - self::TABLE, - new KeySet, - [ - 'transaction' => $this->transactionSelector(), - 'partitionOptions' => $partitionOptionsObj - ] - ], true, $larEnabled), null, '', $grpcConfig); - } - - public function partitionOptions() - { - return [ - [ - [], - new PartitionOptions, - true, - ['routeToLeader' => true] - ], - [ - ['maxPartitions' => 10], - new PartitionOptions([ - 'max_partitions' => 10 - ]), - true, - ['routeToLeader' => true] - ] - ]; - } - - /** - * @dataProvider keysets - */ - public function testFormatKeySet($input, $expected) - { - $this->assertEquals( - $expected, - $this->callPrivateMethod('formatKeySet', [$input]) - ); - } - - public function keysets() - { - return [ - [ - [], - [] - ], [ - [ - 'keys' => [ - [ - 1, - 2 - ] - ] - ], - [ - 'keys' => [ - $this->formatListForApi([1, 2]) - ] - ] - ], [ - [ - 'ranges' => [ - [ - 'startOpen' => [1], - 'endClosed' => [2] - ] - ], - ], [ - 'ranges' => [ - [ - 'startOpen' => $this->formatListForApi([1]), - 'endClosed' => $this->formatListForApi([2]), - ] - ] - ] - ], [ - [ - 'ranges' => [] - ], - [] - ] - ]; - } - - /** - * @dataProvider fieldvalues - */ - public function testFieldValue($input, $expected) - { - $this->assertEquals( - $expected, - $this->callPrivateMethod('fieldValue', [$input]) - ); - } - - public function fieldvalues() - { - return [ - [ - 'foo', - new Value([ - 'string_value' => 'foo' - ]) - ], [ - 1, - new Value([ - 'number_value' => 1 - ]) - ], [ - false, - new Value([ - 'bool_value' => false - ]) - ], [ - null, - new Value([ - 'null_value' => NullValue::NULL_VALUE - ]) - ], [ - [ - 'a' => 'b' - ], - new Value([ - 'struct_value' => new Struct([ - 'fields' => [ - 'a' => new Value([ - 'string_value' => 'b' - ]) - ] - ]) - ]) - ], [ - [ - 'a', 'b', 'c' - ], - new Value([ - 'list_value' => new ListValue([ - 'values' => [ - new Value([ - 'string_value' => 'a' - ]), - new Value([ - 'string_value' => 'b' - ]), - new Value([ - 'string_value' => 'c' - ]), - ] - ]) - ]) - ] - ]; - } - - /** - * @dataProvider transactionOptions - */ - public function testTransactionOptions($input, $expected) - { - // Since the tested method uses pass-by-reference arg, the callPrivateMethod function won't work. - // test on php7 only is better than nothing. - if (version_compare(PHP_VERSION, '7.0.0', '<')) { - $this->markTestSkipped('only works in php 7.'); - return; - } - - $grpc = new Grpc; - $createTransactionSelector = function () { - $args = func_get_args(); - return $this->createTransactionSelector($args[0]); - }; - - $this->assertEquals( - $expected->serializeToJsonString(), - $createTransactionSelector->call($grpc, $input)->serializeToJsonString() - ); - } - - public function transactionOptions() - { - return [ - [ - [ - 'transactionId' => self::TRANSACTION - ], - $this->transactionSelector() - ], [ - [ - 'transaction' => [ - 'singleUse' => [ - 'readWrite' => [] - ] - ] - ], - new TransactionSelector([ - 'single_use' => new TransactionOptions([ - 'read_write' => new ReadWrite - ]) - ]) - ], [ - [ - 'transaction' => [ - 'begin' => [ - 'readWrite' => [] - ] - ] - ], - new TransactionSelector([ - 'begin' => new TransactionOptions([ - 'read_write' => new ReadWrite - ]) - ]) - ] - ]; - } - - public function larOptions() - { - return [ - [ - true, - ['routeToLeader' => true] - ], [ - false, - ['routeToLeader' => false] - ] - ]; - } - - public function testPartialResultSetCustomEncoder() - { - $partialResultSet = new PartialResultSet(); - $partialResultSet->mergeFromJsonString(json_encode([ - 'metadata' => [ - 'transaction' => [ - 'id' => base64_encode(0b00010100) // bytedata is represented as a base64-encoded string in JSON - ], - 'rowType' => [ - 'fields' => [ - ['type' => ['code' => 'INT64']] // enums are represented as their string equivalents in JSON - ] - ], - ], - ])); - - $this->assertEquals(0b00010100, $partialResultSet->getMetadata()->getTransaction()->getId()); - $this->assertEquals(2, $partialResultSet->getMetadata()->getRowType()->getFields()[0]->getType()->getCode()); - - // decode the message and ensure it's decoded as expected - $grpc = new Grpc(); - $serializerProp = new \ReflectionProperty($grpc, 'serializer'); - $serializerProp->setAccessible(true); - $serializer = $serializerProp->getValue($grpc); - $arr = $serializer->encodeMessage($partialResultSet); - - // We expect this to be the binary string - $this->assertEquals(0b00010100, $arr['metadata']['transaction']['id']); - // We expect this to be the integer - $this->assertEquals(2, $arr['metadata']['rowType']['fields'][0]['type']['code']); - } - private function assertCallCorrect( - $method, - array $args, - array $expectedArgs, - $return = null, - $result = '', - $grpcConfig = [] - ) { - $this->requestWrapper->send( - Argument::type('callable'), - $expectedArgs, - Argument::type('array') - )->shouldBeCalled()->willReturn($return ?: $this->successMessage); - - $connection = new Grpc($grpcConfig); - $connection->setRequestWrapper($this->requestWrapper->reveal()); - - $this->assertEquals($result !== '' ? $result : $this->successMessage, $connection->$method($args)); - } - - /** - * Add the resource header to the args list. - * - * @param string $val The header value to add. - * @param array $args The remaining call args. - * @param boolean $append If true, should the last value in $args be an - * array, the header will be appended to that array. If false, the - * header will be added to a separate array. - * @param boolean $lar If true, will add the x-goog-spanner-route-to-leader - * header. - * @param array $options The options to add to the call. - * @return array - */ - private function expectResourceHeader( - $val, - array $args, - $append = true, - $lar = false, - $options = [] - ) { - $header = [ - 'google-cloud-resource-prefix' => [$val] - ]; - if ($lar && !isset($options['readOnly'])) { - $header['x-goog-spanner-route-to-leader'] = ['true']; - } - - $end = end($args); - if (!is_array($end) || !$append) { - $args[]['headers'] = $header; - } elseif (is_array($end)) { - $keys = array_keys($args); - $key = end($keys); - $args[$key]['headers'] = $header; - } - return $args; - } - - private function callPrivateMethod($method, array $args) - { - $grpc = new Grpc; - $ref = new \ReflectionClass($grpc); - - $method = $ref->getMethod($method); - $method->setAccessible(true); - - array_unshift($args, $grpc); - return call_user_func_array([$method, 'invoke'], $args); - } - - private function instanceConfig($full = true) - { - $args = [ - 'name' => self::CONFIG, - 'displayName' => self::CONFIG, - ]; - - if ($full) { - $args = array_merge($args, [ - 'baseConfig' => self::CONFIG, - 'configType' => InstanceConfig\Type::TYPE_UNSPECIFIED, - 'state' => State::CREATING, - 'labels' => [], - 'replicas' => [], - 'optionalReplicas' => [], - 'leaderOptions' => [], - 'reconciling' => false, - ]); - } - - $mask = []; - foreach (array_keys($args) as $key) { - if ($key != "name") { - $mask[] = Serializer::toSnakeCase($key); - } - } - - $fieldMask = $this->serializer->decodeMessage(new FieldMask, ['paths' => $mask]); - - return [ - $args, - $this->serializer->decodeMessage(new InstanceConfig, $args), - $fieldMask - ]; - } - - private function instance($full = true, $nodes = true) - { - $args = [ - 'name' => self::INSTANCE, - 'displayName' => self::INSTANCE, - ]; - - if ($full) { - if ($nodes) { - $args = array_merge($args, [ - 'config' => self::CONFIG, - 'nodeCount' => 1, - 'state' => State::CREATING, - 'labels' => [] - ]); - } else { - $args = array_merge($args, [ - 'config' => self::CONFIG, - 'processingUnits' => 1000, - 'state' => State::CREATING, - 'labels' => [] - ]); - } - } - - $mask = []; - foreach (array_keys($args) as $key) { - if ($key != "name") { - $mask[] = Serializer::toSnakeCase($key); - } - } - - $fieldMask = $this->serializer->decodeMessage(new FieldMask, ['paths' => $mask]); - - return [ - $args, - $this->serializer->decodeMessage(new Instance, $args), - $fieldMask - ]; - } - - private function transactionSelector() - { - return new TransactionSelector([ - 'id' => self::TRANSACTION - ]); - } -} - -//@codingStandardsIgnoreStart -class GrpcStub extends Grpc -{ - public $config; - - protected function constructGapic($gapicName, array $config) - { - $this->config = $config; - - return parent::constructGapic($gapicName, $config); - } -} -//@codingStandardsIgnoreEnd diff --git a/Spanner/tests/Unit/Connection/IamDatabaseTest.php b/Spanner/tests/Unit/Connection/IamDatabaseTest.php deleted file mode 100644 index 782593d18e90..000000000000 --- a/Spanner/tests/Unit/Connection/IamDatabaseTest.php +++ /dev/null @@ -1,69 +0,0 @@ -connection = $this->getConnStub(); - - $this->iam = TestHelpers::stub(IamDatabase::class, [$this->connection->reveal()]); - } - - /** - * @dataProvider methodProvider - */ - public function testMethods($methodName, $proxyName, $args) - { - $this->connection->$proxyName($args) - ->shouldBeCalled() - ->willReturn($args); - - $this->iam->___setProperty('connection', $this->connection->reveal()); - - $res = $this->iam->$methodName($args); - $this->assertEquals($args, $res); - } - - public function methodProvider() - { - $args = ['foo' => 'bar']; - - return [ - ['getPolicy', 'getDatabaseIamPolicy', $args], - ['setPolicy', 'setDatabaseIamPolicy', $args], - ['testPermissions', 'testDatabaseIamPermissions', $args] - ]; - } -} diff --git a/Spanner/tests/Unit/Connection/IamInstanceTest.php b/Spanner/tests/Unit/Connection/IamInstanceTest.php deleted file mode 100644 index 6e7ff67a27aa..000000000000 --- a/Spanner/tests/Unit/Connection/IamInstanceTest.php +++ /dev/null @@ -1,69 +0,0 @@ -connection = $this->getConnStub(); - - $this->iam = TestHelpers::stub(IamInstance::class, [$this->connection->reveal()]); - } - - /** - * @dataProvider methodProvider - */ - public function testMethods($methodName, $proxyName, $args) - { - $this->connection->$proxyName($args) - ->shouldBeCalled() - ->willReturn($args); - - $this->iam->___setProperty('connection', $this->connection->reveal()); - - $res = $this->iam->$methodName($args); - $this->assertEquals($args, $res); - } - - public function methodProvider() - { - $args = ['foo' => 'bar']; - - return [ - ['getPolicy', 'getInstanceIamPolicy', $args], - ['setPolicy', 'setInstanceIamPolicy', $args], - ['testPermissions', 'testInstanceIamPermissions', $args] - ]; - } -} diff --git a/Spanner/tests/Unit/Connection/LongRunningConnectionTest.php b/Spanner/tests/Unit/Connection/LongRunningConnectionTest.php deleted file mode 100644 index 6917e47c3ef5..000000000000 --- a/Spanner/tests/Unit/Connection/LongRunningConnectionTest.php +++ /dev/null @@ -1,70 +0,0 @@ -connection = $this->getConnStub(); - $this->lro = TestHelpers::stub(LongRunningConnection::class, [ - $this->connection->reveal() - ]); - } - - /** - * @dataProvider methodProvider - */ - public function testMethods($methodName, $proxyName, $args) - { - $this->connection->$proxyName($args) - ->shouldBeCalled() - ->willReturn($args); - - $this->lro->___setProperty('connection', $this->connection->reveal()); - - $res = $this->lro->$methodName($args); - $this->assertEquals($args, $res); - } - - public function methodProvider() - { - $args = ['foo' => 'bar']; - - return [ - ['get', 'getOperation', $args], - ['cancel', 'cancelOperation', $args], - ['delete', 'deleteOperation', $args], - ['operations', 'listOperations', $args] - ]; - } -} diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index a63c361fc6a8..1b5735c28756 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -17,22 +17,31 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; +use Google\ApiCore\PagedListResponse; +use Google\ApiCore\Page; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Exception\ServerException; -use Google\Cloud\Core\Iam\Iam; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; +use Google\Cloud\Core\Testing\Snippet\Fixtures; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\Backup; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlResponse; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Connection\ConnectionInterface; use Google\Cloud\Spanner\Connection\Grpc; use Google\Cloud\Spanner\Database; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; @@ -40,23 +49,45 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\BatchWriteRequest\MutationGroup; +use Google\Cloud\Spanner\V1\BeginTransactionRequest; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\CommitRequest; +use Google\Cloud\Spanner\V1\CommitResponse; +use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest; +use Google\Cloud\Spanner\V1\ExecuteBatchDmlResponse; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; +use Google\Cloud\Spanner\V1\Mutation; +use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\TransactionSelector; +use Google\Cloud\Spanner\V1\Transaction as TransactionProto; +use Google\Cloud\Spanner\V1\PartialResultSet; use Google\Cloud\Spanner\V1\ResultSet; +use Google\Cloud\Spanner\V1\ResultSetMetadata; use Google\Cloud\Spanner\V1\ResultSetStats; -use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; use Google\Cloud\Spanner\V1\Session as SessionProto; -use Google\Cloud\Spanner\V1\SpannerClient; -use Google\Cloud\Spanner\V1\Transaction as TransactionProto; +use Google\Cloud\Spanner\V1\StructType; +use Google\Cloud\Spanner\V1\StructType\Field; +use Google\Cloud\Spanner\V1\Type as TypeProto; +use Google\Protobuf\Duration; +use Google\Protobuf\ListValue; +use Google\Protobuf\Timestamp as TimestampProto; +use Google\Protobuf\Value; +use Google\Protobuf\Internal\RepeatedField; +use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; use Google\Cloud\Spanner\V1\TransactionOptions; use Google\Rpc\Code; +use Google\Rpc\Status; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Google\Cloud\Core\Exception\ServiceException; +use Google\LongRunning\Client\OperationsClient; /** * @group spanner @@ -65,10 +96,9 @@ class DatabaseTest extends TestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; + use ApiHelperTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; @@ -81,14 +111,14 @@ class DatabaseTest extends TestCase const TIMESTAMP = '2017-01-09T18:05:22.534799Z'; const BEGIN_RW_OPTIONS = ['begin' => ['readWrite' => []]]; - private $connection; + private $spannerClient; + private $instanceAdminClient; + private $databaseAdminClient; + private $serializer; private $instance; private $sessionPool; - private $lro; - private $lroCallables; private $database; private $session; - private $databaseWithDatabaseRole; private $directedReadOptionsIncludeReplicas; private $directedReadOptionsExcludeReplicas; @@ -97,18 +127,34 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->prophesize(ConnectionInterface::class); - + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]); $this->sessionPool = $this->prophesize(SessionPoolInterface::class); - $this->lro = $this->prophesize(LongRunningConnectionInterface::class); - $this->lroCallables = []; - $this->session = TestHelpers::stub(Session::class, [ - $this->connection->reveal(), + $this->spannerClient = $this->prophesize(SpannerClient::class); + $this->instanceAdminClient = $this->prophesize(InstanceAdminClient::class); + $this->databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + + $this->session = new Session( + $this->spannerClient->reveal(), + $this->serializer, self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION - ]); + ); + $this->directedReadOptionsIncludeReplicas = [ 'includeReplicas' => [ 'autoFailoverDisabled' => false, @@ -123,7 +169,7 @@ public function setUp(): void ]; $this->directedReadOptionsExcludeReplicas = [ 'excludeReplicas' => [ - 'autoFailoverDisabled' => false, + // 'autoFailoverDisabled' => false, 'replicaSelections' => [ [ 'location' => 'us-central1', @@ -133,19 +179,17 @@ public function setUp(): void ] ]; - $this->instance = TestHelpers::stub(Instance::class, [ - $this->connection->reveal(), - $this->lro->reveal(), - $this->lroCallables, + $this->instance = new Instance( + $this->spannerClient->reveal(), + $this->instanceAdminClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, self::PROJECT, self::INSTANCE, false, [], ['directedReadOptions' => $this->directedReadOptionsIncludeReplicas] - ], [ - 'info', - 'connection' - ]); + ); $this->sessionPool->acquire(Argument::type('string')) ->willReturn($this->session); @@ -154,26 +198,23 @@ public function setUp(): void $this->sessionPool->release(Argument::type(Session::class)) ->willReturn(null); - $args = [ - $this->connection->reveal(), + $this->database = new Database( + $this->spannerClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, $this->instance, - $this->lro->reveal(), - $this->lroCallables, self::PROJECT, self::DATABASE, $this->sessionPool->reveal(), false, [], 'Reader' - ]; + ); - $props = [ - 'connection', 'operation', 'session', 'sessionPool', 'instance' - ]; + $this->operationResponse = $this->prophesize(OperationResponse::class); + $this->operationResponse->withResultFunction(Argument::type('callable')) + ->willReturn($this->operationResponse->reveal()); - $this->database = TestHelpers::stub(Database::class, $args, $props); - $args[6] = null; - $this->databaseWithDatabaseRole = TestHelpers::stub(Database::class, $args, $props); } public function testName() @@ -186,17 +227,17 @@ public function testName() public function testInfo() { - $res = [ - 'name' => $this->database->name() - ]; - - $this->connection->getDatabase(Argument::withEntry('name', $this->database->name())) - ->shouldBeCalledTimes(1) - ->willReturn($res); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabase( + Argument::that(function (GetDatabaseRequest $request) { + return $request->getName() === $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new DatabaseProto(['name' => $this->database->name()])); - $this->assertEquals($res, $this->database->info()); + $this->assertArrayHasKey('name', $this->database->info()); + $this->assertEquals($this->database->info()['name'], $this->database->name()); // Make sure the request only is sent once. $this->database->info(); @@ -207,11 +248,14 @@ public function testState() $res = [ 'state' => Database::STATE_READY ]; - $this->connection->getDatabase(Argument::withEntry('name', $this->database->name())) - ->shouldBeCalledTimes(1) - ->willReturn($res); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabase( + Argument::that(function (GetDatabaseRequest $request) { + return $request->getName() === $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new DatabaseProto($res)); $this->assertEquals(Database::STATE_READY, $this->database->state()); @@ -219,51 +263,65 @@ public function testState() $this->database->state(); } + public function testCreateBackup() { $expireTime = new \DateTime(); - $this->connection->createBackup(Argument::allOf( - Argument::withEntry('instance', $this->instance->name()), - Argument::withEntry('backupId', self::BACKUP), - Argument::withEntry('backup', [ - 'database' => $this->database->name(), - 'expireTime' => $expireTime->format('Y-m-d\TH:i:s.u\Z') - ]) - )) - ->shouldBeCalled() - ->willReturn(['name' => 'operations/foo']); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->createBackup( + Argument::that(function ($request) use ($expireTime) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['parent'], + $this->instance->name() + ); + $this->assertEquals($message['backupId'], self::BACKUP); + return $message['backup']['expireTime'] == $expireTime->format('Y-m-d\TH:i:s.u\Z') + && $message['backup']['database'] == $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->database->createBackup(self::BACKUP, $expireTime); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } public function testBackups() { $backups = [ - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup1'), - ], - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup2'), - ] + new Backup(['name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup1')]), + new Backup(['name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup2')]) ]; - $expectedFilter = "database:".$this->database->name(); - $this->connection->listBackups(Argument::withEntry('filter', $expectedFilter)) - ->shouldBeCalled() - ->willReturn(['backups' => $backups]); + $page = $this->prophesize(Page::class); + $page->getResponseObject() + ->willReturn(new ListBackupsResponse(['backups' => $backups])); + $pagedListResponse = $this->prophesize(PagedListResponse::class); + $pagedListResponse->getPage() + ->willReturn($page->reveal()); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $expectedFilter = "database:".$this->database->name(); + $this->databaseAdminClient->listBackups( + Argument::that(function ($request) use ($expectedFilter) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['filter'], + $expectedFilter + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($pagedListResponse->reveal()); $bkps = $this->database->backups(); - $this->assertInstanceOf(ItemIterator::class, $bkps); $bkps = iterator_to_array($bkps); - $this->assertCount(2, $bkps); $this->assertEquals('backup1', DatabaseAdminClient::parseName($bkps[0]->name())['backup']); $this->assertEquals('backup2', DatabaseAdminClient::parseName($bkps[1]->name())['backup']); @@ -271,23 +329,34 @@ public function testBackups() public function testBackupsWithCustomFilter() { - $backups = [ - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup1'), - ], - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup2'), - ] - ]; + $backup1 = DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup1'); + $backup2 = DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, 'backup2'); + $backups = [new Backup(['name' => $backup1]), new Backup(['name' => $backup2])]; + + $page = $this->prophesize(Page::class); + $page->getResponseObject() + ->willReturn(new ListBackupsResponse(['backups' => $backups])); + $pagedListResponse = $this->prophesize(PagedListResponse::class); + $pagedListResponse->getPage() + ->willReturn($page->reveal()); + $defaultFilter = "database:" . $this->database->name(); $customFilter = "customFilter"; $expectedFilter = sprintf('(%1$s) AND (%2$s)', $defaultFilter, $customFilter); - $this->connection->listBackups(Argument::withEntry('filter', $expectedFilter)) - ->shouldBeCalled() - ->willReturn(['backups' => $backups]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->listBackups( + Argument::that(function ($request) use ($expectedFilter) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['filter'], + $expectedFilter + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($pagedListResponse->reveal()); $bkps = $this->database->backups(['filter' => $customFilter]); @@ -306,13 +375,18 @@ public function testReload() 'name' => $this->database->name() ]; - $this->connection->getDatabase(Argument::withEntry('name', $this->database->name())) + $this->databaseAdminClient->getDatabase( + Argument::that(function (GetDatabaseRequest $request) { + return $request->getName() === $this->database->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(2) - ->willReturn($res); - - $this->database->___setProperty('connection', $this->connection->reveal()); + ->willReturn(new DatabaseProto($res)); - $this->assertEquals($res, $this->database->reload()); + $info = $this->database->reload(); + $this->assertArrayHasKey('name', $info); + $this->assertEquals($info['name'], $this->database->name()); // Make sure the request is sent each time the method is called. $this->database->reload(); @@ -323,12 +397,14 @@ public function testReload() */ public function testExists() { - $this->connection->getDatabase(Argument::withEntry( - 'name', - DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) - ))->shouldBeCalled()->willReturn([]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabase( + Argument::that(function (GetDatabaseRequest $request) { + return $request->getName() === $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new DatabaseProto()); $this->assertTrue($this->database->exists()); } @@ -338,12 +414,15 @@ public function testExists() */ public function testExistsNotFound() { - $this->connection->getDatabase(Argument::withEntry('name', $this->database->name())) - ->shouldBeCalled() + $this->databaseAdminClient->getDatabase( + Argument::that(function (GetDatabaseRequest $request) { + return $request->getName() === $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() ->willThrow(new NotFoundException('', 404)); - $this->database->___setProperty('connection', $this->connection->reveal()); - $this->assertFalse($this->database->exists()); } @@ -352,16 +431,27 @@ public function testExistsNotFound() */ public function testCreate() { - $this->connection->createDatabase(Argument::allOf( - Argument::withEntry('createStatement', 'CREATE DATABASE `my-database`'), - Argument::withEntry('extraStatements', [ - 'CREATE TABLE bar' - ]) - ))->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->createDatabase( + Argument::that(function ($request) { + $createStatement = $request->getCreateStatement(); + $extraStatements = $request->getExtraStatements(); + $this->assertStringContainsString('my-database', $createStatement); + $this->assertEquals(['CREATE TABLE bar'], iterator_to_array($extraStatements)); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); + + new OperationResponse('my-operation', new DatabaseAdminClient([ + 'credentials' => Fixtures::KEYFILE_STUB_FIXTURE() + ]), [ + 'lastProtoResponse' => $this->serializer->decodeMessage( + new DatabaseProto(), + ['name' => 'my-database'] + ) + ]); $op = $this->database->create([ 'statements' => [ @@ -369,7 +459,7 @@ public function testCreate() ] ]); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } /** @@ -377,20 +467,23 @@ public function testCreate() */ public function testUpdateDatabase() { - $this->connection->updateDatabase(Argument::allOf( - Argument::withEntry('database', [ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE), - 'enableDropProtection' => true, - ]), - Argument::withEntry('updateMask', ['paths' => ['enable_drop_protection']]) - ))->shouldBeCalledTimes(1)->willReturn([ - 'enableDropProtection' => true - ]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->updateDatabase( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database']['name'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + $this->assertEquals($message['updateMask'], ['paths' => ['enable_drop_protection']]); + return $message['database']['enableDropProtection']; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); - $res = $this->database->updateDatabase(['enableDropProtection' => true]); - $this->assertTrue($res['enableDropProtection']); + $op = $this->database->updateDatabase(['enableDropProtection' => true]); + $this->assertInstanceOf(OperationResponse::class, $op); } /** @@ -400,20 +493,24 @@ public function testCreatePostgresDialect() { $createStatement = sprintf('CREATE DATABASE "%s"', self::DATABASE); - $this->connection->createDatabase(Argument::allOf( - Argument::withEntry('createStatement', $createStatement), - Argument::withEntry('extraStatements', []) - ))->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->createDatabase( + Argument::that( + function ($request) use ($createStatement) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['createStatement'], $createStatement); + $this->assertEmpty($message['extraStatements']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->database->create([ 'databaseDialect'=> DatabaseDialect::POSTGRESQL ]); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } /** @@ -422,20 +519,26 @@ public function testCreatePostgresDialect() public function testRestoreFromBackupName() { $backupName = DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP); - $this->connection->restoreDatabase(Argument::allOf( - Argument::withEntry('instance', $this->instance->name()), - Argument::withEntry('databaseId', self::DATABASE), - Argument::withEntry('backup', $backupName) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->restoreDatabase( + Argument::that( + function ($request) use ($backupName) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['parent'], + $this->instance->name() + ); + $this->assertEquals($message['databaseId'], self::DATABASE); + $this->assertEquals($message['backup'], $backupName); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->database->restore($backupName); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } /** @@ -445,20 +548,25 @@ public function testRestoreFromBackupObject() { $backupObj = $this->instance->backup(self::BACKUP); - $this->connection->restoreDatabase(Argument::allOf( - Argument::withEntry('instance', $this->instance->name()), - Argument::withEntry('databaseId', self::DATABASE), - Argument::withEntry('backup', $backupObj->name()) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->restoreDatabase( + Argument::that( + function ($request) use ($backupObj) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['parent'], + $this->instance->name() + ); + $this->assertEquals($message['databaseId'], self::DATABASE); + $this->assertEquals($message['backup'], $backupObj->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->database->restore($backupObj); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } /** @@ -467,34 +575,48 @@ public function testRestoreFromBackupObject() public function testUpdateDdl() { $statement = 'foo'; - $this->connection->updateDatabaseDdl([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE), - 'statements' => [$statement] - ])->willReturn([ - 'name' => 'my-operation' - ]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->updateDatabaseDdl( + Argument::that( + function ($request) use ($statement) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + $this->assertEquals($message['statements'], [$statement]); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $res = $this->database->updateDdl($statement); - $this->assertInstanceOf(LongRunningOperation::class, $res); + $this->assertInstanceOf(OperationResponse::class, $res); } - /** * @group spanner-admin */ public function testUpdateDdlBatch() { $statements = ['foo', 'bar']; - $this->connection->updateDatabaseDdl([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE), - 'statements' => $statements - ])->willReturn([ - 'name' => 'my-operation' - ])->shouldBeCalled(); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->updateDatabaseDdl( + Argument::that( + function ($request) use ($statements) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + $this->assertEquals($message['statements'], $statements); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $this->database->updateDdlBatch($statements); } @@ -505,15 +627,25 @@ public function testUpdateDdlBatch() public function testUpdateWithSingleStatement() { $statement = 'foo'; - $this->connection->updateDatabaseDdl([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE), - 'statements' => ['foo'] - ])->shouldBeCalled()->willReturn(['name' => 'operations/foo']); - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->updateDatabaseDdl( + Argument::that( + function ($request) use ($statement) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + $this->assertEquals($message['statements'], [$statement]); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $res = $this->database->updateDdl($statement); - $this->assertInstanceOf(LongRunningOperation::class, $res); + $this->assertInstanceOf(OperationResponse::class, $res); } /** @@ -521,14 +653,21 @@ public function testUpdateWithSingleStatement() */ public function testDrop() { - $this->connection->dropDatabase([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) - ])->shouldBeCalled(); + $this->databaseAdminClient->dropDatabase( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); $this->sessionPool->clear()->shouldBeCalled()->willReturn(null); - $this->database->___setProperty('connection', $this->connection->reveal()); - $this->database->drop(); } @@ -537,53 +676,56 @@ public function testDrop() */ public function testDropDeleteSession() { - $this->connection->createSession(Argument::withEntry('database', $this->database->name())) - ->shouldBeCalled() - ->willReturn([ - 'name' => $this->session->name() - ]); - - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) - ->shouldBeCalled() - ->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->createSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['database'] == $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new SessionProto(['name' => $this->session->name()])); - $this->connection->deleteSession(Argument::allOf( - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('name', $this->session->name()) - )) - ->shouldBeCalled(); + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $this->connection->dropDatabase([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) - ])->shouldBeCalled(); + $this->spannerClient->deleteSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['name'] == $this->session->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); + + $this->databaseAdminClient->dropDatabase( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); - $database = TestHelpers::stub(Database::class, [ - $this->connection->reveal(), + $database = new Database( + $this->spannerClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, $this->instance, - $this->lro->reveal(), - $this->lroCallables, self::PROJECT, self::DATABASE - ]); + ); // This will set a session on the Database class. $database->transaction(); @@ -597,11 +739,19 @@ public function testDropDeleteSession() public function testDdl() { $ddl = ['create table users', 'create table posts']; - $this->connection->getDatabaseDDL([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) - ])->willReturn(['statements' => $ddl]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabaseDdl( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseDdlResponse(['statements' => $ddl])); $this->assertEquals($ddl, $this->database->ddl()); } @@ -611,11 +761,19 @@ public function testDdl() */ public function testDdlNoResult() { - $this->connection->getDatabaseDDL([ - 'name' => DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) - ])->willReturn([]); - - $this->database->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabaseDdl( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['database'], + DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseDdlResponse()); $this->assertEquals([], $this->database->ddl()); } @@ -625,26 +783,20 @@ public function testDdlNoResult() */ public function testIam() { - $this->assertInstanceOf(Iam::class, $this->database->iam()); + $this->assertInstanceOf(IamManager::class, $this->database->iam()); } public function testSnapshot() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) - ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $res = $this->database->snapshot(); $this->assertInstanceOf(Snapshot::class, $res); @@ -671,13 +823,9 @@ public function testSnapshotNestedTransaction() // Begin transaction RPC is skipped when begin is inlined // and invoked only if `begin` fails or if commit is the // sole operation in the transaction. - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); - - $this->connection->rollback(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->database->runTransaction(function ($t) { $this->database->snapshot(); @@ -686,28 +834,26 @@ public function testSnapshotNestedTransaction() public function testBatchWrite() { - $expectedMutationGroup = ['mutations' => [ - [ - Operation::OP_INSERT_OR_UPDATE => [ - 'table' => 'foo', - 'columns' => ['bar1', 'bar2'], - 'values' => [1, 2] - ] - ] - ]]; - $this->connection->batchWrite(Argument::allOf( - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('session', $this->session->name()), - Argument::withEntry('mutationGroups', [$expectedMutationGroup]) - ))->shouldBeCalled()->willReturn(['foo result']); - + $expectedMutationGroup = new MutationGroup(['mutations' => [ + new Mutation(['insert_or_update' => new Mutation\Write([ + 'table' => 'foo', + 'columns' => ['bar1', 'bar2'], + 'values' => [new ListValue(['values' => [ + new Value(['string_value' => '1']), + new Value(['string_value' => '2']), + ]])] + ])]) + ]]); + + $this->spannerClient->batchWrite( + Argument::that(function ($request) use ($expectedMutationGroup) { + return $request->getSession() === $this->session->name() + && $request->getMutationGroups()[0] == $expectedMutationGroup; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $mutationGroups = [ ($this->database->mutationGroup(false)) @@ -717,49 +863,13 @@ public function testBatchWrite() ) ]; - $this->refreshOperation($this->database, $this->connection->reveal()); - $result = $this->database->batchWrite($mutationGroups); - $this->assertIsArray($result); + $this->assertEquals('10', iterator_to_array($result)[0]['values'][0]); } public function testRunTransaction() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - ]) - )) - ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - ]) - )) - ->shouldBeCalled() - ->willReturn(['commitTimestamp' => '2017-01-09T18:05:22.534799Z']); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->stubCommit(false); $hasTransaction = false; @@ -776,12 +886,9 @@ public function testRunTransactionNoCommit() { $this->expectException(\InvalidArgumentException::class); - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); - - $this->connection->rollback(Argument::any())->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->database->runTransaction($this->noop()); } @@ -790,13 +897,9 @@ public function testRunTransactionNestedTransaction() { $this->expectException(\BadMethodCallException::class); - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->rollback(Argument::any()) - ->shouldNotBeCalled(); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->database->runTransaction(function ($t) { $this->database->runTransaction($this->noop()); @@ -809,23 +912,19 @@ public function testRunTransactionShouldRetryOnRstStreamErrors() $this->expectExceptionMessage('RST_STREAM'); $err = new ServerException('RST_STREAM', Code::INTERNAL); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(3) ->willThrow($err); $this->database->runTransaction(function ($t) { $t->commit(); - }, ['maxRetries' => 2]); + }, ['retrySettings' => ['maxRetries' => 2]]); } public function testRunTransactionRetry() @@ -839,51 +938,41 @@ public function testRunTransactionRetry() ] ]); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(3) - ->willReturn(['id' => self::TRANSACTION]); + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $it = 0; - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $commitResponse = $this->commitResponse(); + $this->spannerClient->commit( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(3) - ->will(function () use (&$it, $abort) { + ->will(function () use (&$it, $abort, $commitResponse) { $it++; if ($it <= 2) { throw $abort; } - return ['commitTimestamp' => TransactionTest::TIMESTAMP]; + return $commitResponse; }); - $this->refreshOperation($this->database, $this->connection->reveal()); - $this->database->runTransaction(function ($t) use (&$it) { if ($it > 0) { $this->assertTrue($t->isRetry()); } else { $this->assertFalse($t->isRetry()); } - $t->commit(); }); } @@ -901,45 +990,33 @@ public function testRunTransactionAborted() ] ]); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(Database::MAX_RETRIES + 1) - ->willReturn(['id' => self::TRANSACTION]); + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $it = 0; - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->commit( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(Database::MAX_RETRIES + 1) ->will(function () use (&$it, $abort) { $it++; - if ($it <= Database::MAX_RETRIES + 1) { throw $abort; } - return ['commitTimestamp' => TransactionTest::TIMESTAMP]; }); - $this->refreshOperation($this->database, $this->connection->reveal()); - $this->database->runTransaction(function ($t) { $t->commit(); }); @@ -947,24 +1024,19 @@ public function testRunTransactionAborted() public function testTransaction() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - ]) - )) - ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['requestOptions']['transactionTag' ], + self::TRANSACTION_TAG, + ); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $t = $this->database->transaction(['tag' => self::TRANSACTION_TAG]); $this->assertInstanceOf(Transaction::class, $t); @@ -974,13 +1046,9 @@ public function testTransactionNestedTransaction() { $this->expectException(\BadMethodCallException::class); - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->rollback(Argument::any()) - ->shouldNotBeCalled(); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->database->runTransaction(function ($t) { $this->database->transaction(); @@ -992,23 +1060,28 @@ public function testInsert() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][OPERATION::OP_INSERT]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][OPERATION::OP_INSERT]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][OPERATION::OP_INSERT]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_INSERT]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->insert($table, $row); $this->assertInstanceOf(Timestamp::class, $res); @@ -1020,23 +1093,28 @@ public function testInsertBatch() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][OPERATION::OP_INSERT]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][OPERATION::OP_INSERT]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][OPERATION::OP_INSERT]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_INSERT]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->insertBatch($table, [$row]); $this->assertInstanceOf(Timestamp::class, $res); @@ -1048,23 +1126,28 @@ public function testUpdate() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_UPDATE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_UPDATE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_UPDATE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_UPDATE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_UPDATE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_UPDATE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->update($table, $row); $this->assertInstanceOf(Timestamp::class, $res); @@ -1076,23 +1159,28 @@ public function testUpdateBatch() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_UPDATE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_UPDATE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_UPDATE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_UPDATE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_UPDATE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_UPDATE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->updateBatch($table, [$row]); $this->assertInstanceOf(Timestamp::class, $res); @@ -1104,23 +1192,28 @@ public function testInsertOrUpdate() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->insertOrUpdate($table, $row); $this->assertInstanceOf(Timestamp::class, $res); @@ -1132,23 +1225,28 @@ public function testInsertOrUpdateBatch() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_INSERT_OR_UPDATE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_INSERT_OR_UPDATE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->insertOrUpdateBatch($table, [$row]); $this->assertInstanceOf(Timestamp::class, $res); @@ -1160,23 +1258,28 @@ public function testReplace() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_REPLACE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_REPLACE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_REPLACE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_REPLACE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_REPLACE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_REPLACE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->replace($table, $row); $this->assertInstanceOf(Timestamp::class, $res); @@ -1188,23 +1291,28 @@ public function testReplaceBatch() $table = 'foo'; $row = ['col' => 'val']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $row) { - if ($arg['mutations'][0][Operation::OP_REPLACE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $row) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_REPLACE]['columns'][0] !== array_keys($row)[0]) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_REPLACE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_REPLACE]['values'][0] !== current($row)) { - return false; - } + if ($request['mutations'][0][OPERATION::OP_REPLACE]['columns'][0] !== array_keys($row)[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][OPERATION::OP_REPLACE]['values'][0][0] !== current($row)) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->replaceBatch($table, [$row]); $this->assertInstanceOf(Timestamp::class, $res); @@ -1216,23 +1324,28 @@ public function testDelete() $table = 'foo'; $keys = [10, 'bar']; - $this->connection->commit(Argument::that(function ($arg) use ($table, $keys) { - if ($arg['mutations'][0][Operation::OP_DELETE]['table'] !== $table) { - return false; - } + $this->spannerClient->commit( + Argument::that(function ($request) use ($table, $keys) { + $request = $this->serializer->encodeMessage($request); - if ($arg['mutations'][0][Operation::OP_DELETE]['keySet']['keys'][0] !== (string) $keys[0]) { - return false; - } + if ($request['mutations'][0][Operation::OP_DELETE]['table'] !== $table) { + return false; + } - if ($arg['mutations'][0][Operation::OP_DELETE]['keySet']['keys'][1] !== $keys[1]) { - return false; - } + if ($request['mutations'][0][Operation::OP_DELETE]['keySet']['keys'][0][0] !== (string) $keys[0]) { + return false; + } - return true; - }))->shouldBeCalled()->willReturn($this->commitResponse()); + if ($request['mutations'][0][Operation::OP_DELETE]['keySet']['keys'][1][0] !== $keys[1]) { + return false; + } - $this->refreshOperation($this->database, $this->connection->reveal()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $res = $this->database->delete($table, new KeySet(['keys' => $keys])); $this->assertInstanceOf(Timestamp::class, $res); @@ -1243,12 +1356,23 @@ public function testExecute() { $sql = 'SELECT * FROM Table'; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($sql) { + return $request->getSql() == $sql; + }), + Argument::that(function ($callOptions) { + $this->assertArrayHasKey('headers', $callOptions); + $this->assertArrayHasKey('x-goog-spanner-route-to-leader', $callOptions['headers']); + $this->assertEquals(['true'], $callOptions['headers']['x-goog-spanner-route-to-leader']); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_lower_bound' => 1]), + self::TRANSACTION + )); $res = $this->database->execute($sql, [ 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE @@ -1260,16 +1384,17 @@ public function testExecute() public function testExecuteWithSingleSession() { - $this->database->___setProperty('sessionPool', null); - $this->database->___setProperty('session', $this->session); $sql = 'SELECT * FROM Table'; $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $this->connection->executeStreamingSql(Argument::withEntry('session', $sessName)) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($sessName) { + return $request->getSession() == $sessName; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->database->execute($sql); $rows = iterator_to_array($res->rows()); @@ -1277,19 +1402,20 @@ public function testExecuteWithSingleSession() public function testExecuteSingleUseMaxStaleness() { - $this->database->___setProperty('sessionPool', null); - $this->database->___setProperty('session', $this->session); $sql = 'SELECT * FROM Table'; $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $this->connection->executeStreamingSql(Argument::withEntry('session', $sessName)) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($sessName) { + return $request->getSession() == $sessName; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->database->execute($sql, [ - 'maxStaleness' => new Duration(10, 0) + 'maxStaleness' => new Duration(['seconds' => 10, 'nanos' => 0]) ]); $rows = iterator_to_array($res->rows()); } @@ -1298,35 +1424,52 @@ public function testExecuteBeginMaxStalenessFails() { $this->expectException(\BadMethodCallException::class); - $this->database->___setProperty('sessionPool', null); - $this->database->___setProperty('session', $this->session); $sql = 'SELECT * FROM Table'; $this->database->execute($sql, [ 'begin' => true, - 'maxStaleness' => new Duration(10, 0) + 'maxStaleness' => new Duration(['seconds' => 10, 'nanos' => 0]) ]); } public function testExecutePartitionedUpdate() { $sql = 'UPDATE foo SET bar = @bar'; - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('transactionOptions', [ - 'partitionedDml' => [] - ]), - Argument::withEntry('singleUse', false) - ))->shouldBeCalled()->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['options']['partitionedDml' ], + [] + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true)); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['sql'], $sql); + $this->assertEquals($message['transaction'], ['id' => self::TRANSACTION]); + return true; + }), + Argument::that(function ($callOptions) { + $this->assertArrayHasKey('headers', $callOptions); + $this->assertArrayHasKey('x-goog-spanner-route-to-leader', $callOptions['headers']); + $this->assertEquals(['true'], $callOptions['headers']['x-goog-spanner-route-to-leader']); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_lower_bound' => 1]), + true + )); - $this->refreshOperation($this->database, $this->connection->reveal()); $res = $this->database->executePartitionedUpdate($sql); $this->assertEquals(1, $res); @@ -1337,27 +1480,21 @@ public function testRead() $table = 'Table'; $opts = ['foo' => 'bar']; - $this->connection->streamingRead(Argument::that(function ($arg) use ($table) { - if ($arg['table'] !== $table) { - return false; - } - - if ($arg['keySet']['all'] !== true) { - return false; - } - - if ($arg['columns'] !== ['ID']) { - return false; - } - - if ($arg['headers'] !== ['x-goog-spanner-route-to-leader' => ['true']]) { - return false; - } - - return true; - }))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->streamingRead( + Argument::that(function (ReadRequest $request) use ($table) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($request->getTable(), $table); + $this->assertEquals( + $message['keySet'], + ['all' => true, 'keys' => [], 'ranges' => []] + ); + $this->assertEquals($message['columns'], ['ID']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->database->read( $table, @@ -1377,57 +1514,82 @@ public function testSessionPool() public function testClose() { + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + $this->sessionPool->release(Argument::type(Session::class)) ->shouldBeCalled() ->willReturn(null); - $this->database->___setProperty('sessionPool', $this->sessionPool->reveal()); - $this->database->___setProperty('session', $this->session); + // start a transaction to create a session + $this->database->transaction(); $this->database->close(); - - $this->assertNull($this->database->___getProperty('session')); } public function testCloseNoPool() { - $this->connection->deleteSession(Argument::allOf( - Argument::withEntry('name', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) - ->shouldBeCalled() - ->willReturn([]); + $database = new Database( + $this->spannerClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance, + self::PROJECT, + self::DATABASE + ); + + $this->spannerClient->createSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['database'] == $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new SessionProto(['name' => $this->session->name()])); + + $this->spannerClient->deleteSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['name'] == $this->session->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); + + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $this->session->___setProperty('connection', $this->connection->reveal()); - $this->database->___setProperty('sessionPool', null); - $this->database->___setProperty('session', $this->session); + // start a transaction to create a session + $database->transaction(); $this->database->close(); } public function testCreateSession() { - $db = SpannerClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE); - $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $this->connection->createSession(Argument::withEntry('database', $db)) - ->shouldBeCalled() - ->willReturn([ - 'name' => $sessName - ]); - - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->createSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['database'] == $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new SessionProto(['name' => $this->session->name()])); $sess = $this->database->createSession(); $this->assertInstanceOf(Session::class, $sess); - $this->assertEquals($sessName, $sess->name()); + $this->assertEquals($this->session->name(), $sess->name()); } public function testSession() @@ -1452,22 +1614,22 @@ public function testIdentity() ], $this->database->identity()); } - public function testConnection() - { - $this->assertInstanceOf(ConnectionInterface::class, $this->database->connection()); - } - // ******* // Helpers private function commitResponse() { - return ['commitTimestamp' => '2017-01-09T18:05:22.534799Z']; + return new CommitResponse([ + 'commit_timestamp' => new TimestampProto([ + 'seconds' => (new \DateTime(self::TIMESTAMP))->format('U'), + 'nanos' => 534799000 + ]) + ]); } private function assertTimestampIsCorrect($res) { - $ts = new \DateTimeImmutable($this->commitResponse()['commitTimestamp']); + $ts = new \DateTimeImmutable(self::TIMESTAMP); $this->assertEquals($ts->format('Y-m-d\TH:i:s\Z'), $res->get()->format('Y-m-d\TH:i:s\Z')); } @@ -1481,29 +1643,63 @@ private function noop() public function testDBDatabaseRole() { + $this->spannerClient->createSession( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['session']['creatorRole'], 'Reader'); + return $message['database'] == $this->database->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new SessionProto(['name' => $this->session->name()])); + $sql = $this->createStreamingAPIArgs()['sql']; - $this->connection->createSession(Argument::withEntry( - 'session', - ['labels' => [], 'creator_role' => 'Reader'] - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => $this->session->name() - ]); - $this->connection->executeStreamingSql(Argument::withEntry('sql', $sql)) - ->shouldBeCalled()->willReturn($this->resultGenerator()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $this->assertEquals($request->getSql(), $sql); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); + + $this->spannerClient->deleteSession( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') + )->shouldBeCalledOnce(); - $this->databaseWithDatabaseRole->execute($sql); + $databaseWithDatabaseRole = new Database( + $this->spannerClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, + $this->instance, + self::PROJECT, + self::DATABASE, + null, + false, + [], + 'Reader' + ); + $databaseWithDatabaseRole->execute($sql); } public function testExecuteWithDirectedRead() { - $this->connection->executeStreamingSql(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsIncludeReplicas - )) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsIncludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $sql = 'SELECT * FROM Table'; $res = $this->database->execute($sql); @@ -1514,12 +1710,19 @@ public function testExecuteWithDirectedRead() public function testPrioritizeExecuteDirectedReadOptions() { - $this->connection->executeStreamingSql(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsExcludeReplicas - )) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsExcludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $sql = 'SELECT * FROM Table'; $res = $this->database->execute( @@ -1536,12 +1739,19 @@ public function testReadWithDirectedRead() $table = 'foo'; $keys = [10, 'bar']; $columns = ['id', 'name']; - $this->connection->streamingRead(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsIncludeReplicas - )) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsIncludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->database->read( $table, @@ -1558,12 +1768,19 @@ public function testPrioritizeReadDirectedReadOptions() $table = 'foo'; $keys = [10, 'bar']; $columns = ['id', 'name']; - $this->connection->streamingRead(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsExcludeReplicas - )) - ->shouldBeCalled() - ->willReturn($this->resultGenerator()); + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsExcludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->database->read( $table, @@ -1582,7 +1799,6 @@ public function testRunTransactionWithUpdate() $this->stubCommit(); $this->stubExecuteStreamingSql(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($sql) { $t->executeUpdate($sql); @@ -1597,7 +1813,6 @@ public function testRunTransactionWithQuery() $this->stubCommit(); $this->stubExecuteStreamingSql(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($sql) { $t->execute($sql)->rows()->current(); @@ -1613,7 +1828,6 @@ public function testRunTransactionWithRead() $this->stubCommit(); $this->stubStreamingRead(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($keySet, $cols) { $t->read(self::TEST_TABLE_NAME, $keySet, $cols)->rows()->current(); @@ -1628,7 +1842,6 @@ public function testRunTransactionWithUpdateBatch() $this->stubCommit(); $this->stubExecuteBatchDml(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($sql) { $t->executeUpdateBatch([['sql' => $sql]]); @@ -1646,7 +1859,6 @@ public function testRunTransactionWithReadFirst() $this->stubCommit(); $this->stubStreamingRead(); $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($keySet, $cols, $sql) { $t->read(self::TEST_TABLE_NAME, $keySet, $cols)->rows()->current(); @@ -1665,7 +1877,6 @@ public function testRunTransactionWithExecuteFirst() $this->stubCommit(); $this->stubStreamingRead(['id' => self::TRANSACTION]); $this->stubExecuteStreamingSql(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($keySet, $cols, $sql) { $t->execute($sql)->rows()->current(); @@ -1685,7 +1896,6 @@ public function testRunTransactionWithUpdateBatchFirst() $this->stubExecuteBatchDml(); $this->stubStreamingRead(['id' => self::TRANSACTION]); $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function (Transaction $t) use ($keySet, $cols, $sql) { $t->executeUpdateBatch([['sql' => $sql]]); @@ -1705,16 +1915,32 @@ public function testRunTransactionWithUpdateBatchError() $this->stubCommit(); $this->stubStreamingRead(['id' => self::TRANSACTION]); $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); - $this->refreshOperation($this->database, $this->connection->reveal()); - $this->connection->executeBatchDml(Argument::allOf( - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]), - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - ))->shouldBeCalled()->willReturn([ - 'status' => ['code' => Code::INVALID_ARGUMENT], - 'resultSets' => [['metadata' => ['transaction' => ['id' => self::TRANSACTION]]]] - ]); + $this->spannerClient->executeBatchDml( + Argument::that(function (ExecuteBatchDmlRequest $request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['requestOptions']['transactionTag'], + self::TRANSACTION_TAG + ); + $this->assertEquals( + $message['transaction'], + ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteBatchDmlResponse([ + 'status' => new Status(['code' => Code::INVALID_ARGUMENT]), + 'result_sets' => [ + new ResultSet([ + 'metadata' => new ResultSetMetadata([ + 'transaction' => new TransactionProto(['id' => self::TRANSACTION]) + ]) + ]) + ] + ])); $this->database->runTransaction(function (Transaction $t) use ($keySet, $cols, $sql) { $result = $t->executeUpdateBatch([['sql' => $sql], ['sql' => $sql]]); @@ -1732,17 +1958,44 @@ public function testRunTransactionWithFirstFailedStatement() $error = new ServerException('RST_STREAM', Code::INTERNAL); // First call with ILB fails - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]) - ))->shouldBeCalled()->willThrow($error); - $this->stubCommit(false); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($sql, $message['sql']); + $this->assertEquals( + $message['requestOptions']['transactionTag'], + self::TRANSACTION_TAG + ); + return $message['transaction'] == ['begin' => + ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false] + ]; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willThrow($error); + // Second call with non ILB return result - $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($sql, $message['sql']); + $this->assertEquals( + $message['requestOptions']['transactionTag'], + self::TRANSACTION_TAG + ); + return $message['transaction'] == ['id' => self::TRANSACTION]; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_exact' => 1]), + self::TRANSACTION + )); + + $this->stubCommit(false); $this->database->runTransaction(function ($t) use ($sql) { $t->execute($sql); @@ -1768,42 +2021,34 @@ public function testRunTransactionWithCommitAborted() $this->stubExecuteStreamingSql(); // Second onwards non ILB $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes($numOfRetries) - ->willReturn(['id' => self::TRANSACTION]); + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $it = 0; - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $commitResponse = $this->commitResponse(); + $this->spannerClient->commit( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['session'] == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalledTimes($numOfRetries + 1) - ->will(function () use (&$it, $abort, $numOfRetries) { + ->will(function () use (&$it, $abort, $numOfRetries, $commitResponse) { $it++; if ($it <= $numOfRetries) { throw $abort; } - return ['commitTimestamp' => TransactionTest::TIMESTAMP]; + return $commitResponse; }); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function ($t) use ($sql) { $t->execute($sql); @@ -1817,28 +2062,35 @@ public function testRunTransactionWithBeginTransactionFailure() $error = new ServerException('RST_STREAM', Code::INTERNAL); $sql = $this->createStreamingAPIArgs()['sql']; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]) - ))->shouldBeCalled()->willThrow($error); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ) - )) + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($sql, $message['sql']); + $this->assertEquals( + $message['requestOptions']['transactionTag'], + self::TRANSACTION_TAG + ); + return $message['transaction'] == ['begin' => + ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false] + ]; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willThrow($error); + + $this->spannerClient->beginTransaction( + Argument::that(function ($request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['session'], $this->session->name()); + return true; + }), + Argument::type('array') + ) ->shouldBeCalledTimes(Database::MAX_RETRIES) ->willThrow($error); - $this->connection->commit(Argument::any())->shouldNotBeCalled(); - $this->refreshOperation($this->database, $this->connection->reveal()); + + $this->spannerClient->commit(Argument::cetera())->shouldNotBeCalled(); $this->database->runTransaction(function ($t) use ($sql) { $t->execute($sql); @@ -1849,7 +2101,6 @@ public function testRunTransactionWithBeginTransactionFailure() public function testRunTransactionWithBlindCommit() { $this->stubCommit(false); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function ($t) { $t->insert('Posts', [ @@ -1866,18 +2117,27 @@ public function testRunTransactionWithUnavailableErrorRetry() $sql = $this->createStreamingAPIArgs()['sql']; $numOfRetries = 2; $unavailable = new ServiceException('Unavailable', 14); - $result = $this->resultGenerator(true, self::TRANSACTION); + $result = $this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_lower_bound' => 1]), + self::TRANSACTION + ); $it = 0; // First call with ILB results in unavailable error. // Second call also made with ILB, returns ResultSet. - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]) - )) + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['sql'], $sql); + $this->assertEquals( + $message['transaction'], + ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] + ); + return $message['requestOptions']['transactionTag'] == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) ->shouldBeCalledTimes($numOfRetries) ->will(function () use (&$it, $unavailable, $numOfRetries, $result) { $it++; @@ -1886,8 +2146,8 @@ public function testRunTransactionWithUnavailableErrorRetry() } return $result; }); + $this->stubCommit(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function ($t) use ($sql) { $t->execute($sql); @@ -1899,22 +2159,29 @@ public function testRunTransactionWithFirstUnavailableErrorRetry() { $sql = $this->createStreamingAPIArgs()['sql']; $unavailable = new ServiceException('Unavailable', 14); + $stream = $this->prophesize(ServerStream::class); + $stream->readAll() + ->willReturn($this->resultGeneratorWithError()); // First call with ILB results in a transaction. // Then the stream fails, Second call needs to use the // transaction created by the first call. - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]) - )) - ->shouldBeCalledTimes(1) - ->willreturn($this->resultGeneratorWithError()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $this->assertEquals($request->getSql(), $sql); + return $this->serializer->decodeMessage( + new TransactionSelector(), + self::BEGIN_RW_OPTIONS + ) == $request->getTransaction() + && $request->getRequestOptions()->getTransactionTag() == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($stream->reveal()); + $this->stubExecuteStreamingSql(['id' => self::TRANSACTION]); $this->stubCommit(); - $this->refreshOperation($this->database, $this->connection->reveal()); $this->database->runTransaction(function ($t) use ($sql) { $result = $t->execute($sql); @@ -1942,14 +2209,17 @@ public function testRunTransactionWithUnavailableAndAbortErrorRetry() $it = 0; // First call with ILB results in unavailable error. // Second call also made with ILB, gets aborted. - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('transaction', self::BEGIN_RW_OPTIONS), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]), - Argument::withEntry('table', self::TEST_TABLE_NAME), - Argument::withEntry('columns', $cols) - )) + $this->spannerClient->streamingRead( + Argument::that(function ($request) use ($cols) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['table'], self::TEST_TABLE_NAME); + $this->assertEquals($message['columns'], $cols); + return $message['transaction'] + == ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] + && $message['requestOptions']['transactionTag'] == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) ->shouldBeCalledTimes($numOfRetries) ->will(function () use (&$it, $unavailable, $numOfRetries, $abort) { $it++; @@ -1959,34 +2229,32 @@ public function testRunTransactionWithUnavailableAndAbortErrorRetry() throw $abort; } }); + // Should retry with beginTransaction RPC. $this->stubStreamingRead(['id' => self::TRANSACTION]); - $this->connection->beginTransaction(Argument::any()) - ->willReturn(['id' => self::TRANSACTION]) - ->shouldBeCalled(); - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - ]), - Argument::withEntry('transactionId', self::TRANSACTION), - Argument::withEntry('mutations', [['insert' => [ - 'table' => self::TEST_TABLE_NAME, - 'columns' => ['ID', 'title', 'content'], - 'values' => ['10', 'My New Post', 'Hello World'] - ]]]) - )) - ->shouldBeCalledTimes(1) - ->willReturn(['commitTimestamp' => self::TIMESTAMP]); - $this->refreshOperation($this->database, $this->connection->reveal()); + + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals($request->getSession(), $this->session->name()); + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); + $this->assertEquals($request->getRequestOptions()->getTransactionTag(), self::TRANSACTION_TAG); + $this->assertEquals($this->serializer->encodeMessage($request)['mutations'], [['insert' => [ + 'table' => self::TEST_TABLE_NAME, + 'columns' => ['ID', 'title', 'content'], + 'values' => [['10', 'My New Post', 'Hello World']] + ]]]); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); $this->database->runTransaction(function ($t) use ($keySet, $cols) { $t->insert(self::TEST_TABLE_NAME, [ @@ -2004,10 +2272,13 @@ public function testRunTransactionWithRollback() $sql = $this->createStreamingAPIArgs()['sql']; $this->stubExecuteStreamingSql(); - $this->connection->rollback(Argument::allOf( - Argument::withEntry('transactionId', self::TRANSACTION) - ))->shouldBeCalled()->willReturn(null); - $this->refreshOperation($this->database, $this->connection->reveal()); + $this->spannerClient->rollback( + Argument::that(function ($request) use ($sql) { + return $request->getTransactionId() == self::TRANSACTION; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); $this->database->runTransaction(function (Transaction $t) use ($sql) { $t->execute($sql); @@ -2153,22 +2424,20 @@ private function createStreamingAPIArgs() private function resultGeneratorWithError() { - $fields = [ + $fields = new Field([ 'name' => 'ID', - 'value' => ['code' => Database::TYPE_INT64] - ]; - $values = [10]; - $result = [ - 'metadata' => [ - 'rowType' => [ - 'fields' => $fields - ] - ], + 'type' => new TypeProto(['code' => Database::TYPE_INT64]) + ]); + $values = [new Value(['number_value' => 10])]; + $result = new PartialResultSet([ + 'metadata' => new ResultSetMetadata([ + 'row_type' => new StructType([ + 'fields' => [$fields] + ]), + 'transaction' => new TransactionProto(['id' => self::TRANSACTION]) + ]), 'values' => $values - ]; - $result['metadata']['transaction'] = [ - 'id' => self::TRANSACTION - ]; + ]); yield $result; throw new ServiceException('Unavailable', 14); @@ -2177,67 +2446,97 @@ private function resultGeneratorWithError() private function stubCommit($withTransaction = true) { if ($withTransaction) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); } else { - $this->connection->beginTransaction(Argument::any()) - ->willReturn(['id' => self::TRANSACTION]) - ->shouldBeCalled(); + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); } - $this->connection->commit(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry( - 'database', - DatabaseAdminClient::databaseName( - self::PROJECT, - self::INSTANCE, - self::DATABASE - ) - ), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - ]), - Argument::withEntry('transactionId', self::TRANSACTION) - )) - ->shouldBeCalled() - ->willReturn(['commitTimestamp' => self::TIMESTAMP]); + + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals($request->getSession(), $this->session->name()); + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); + $this->assertEquals($request->getRequestOptions()->getTransactionTag(), self::TRANSACTION_TAG); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); } private function stubStreamingRead($transactionOptions = self::BEGIN_RW_OPTIONS) { - $keySet = $this->createStreamingAPIArgs()['keySet']; $cols = $this->createStreamingAPIArgs()['cols']; - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('transaction', $transactionOptions), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]), - Argument::withEntry('table', self::TEST_TABLE_NAME), - Argument::withEntry('columns', $cols) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true, self::TRANSACTION)); + $this->spannerClient->streamingRead( + Argument::that(function (ReadRequest $request) use ($transactionOptions, $cols) { + return $request->getTransaction() == $this->serializer->decodeMessage( + new TransactionSelector(), + $transactionOptions + ) + && $request->getTable() == self::TEST_TABLE_NAME + && iterator_to_array($request->getColumns()) == $cols + && $request->getRequestOptions()->getTransactionTag() == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_exact' => 1]), + self::TRANSACTION + )); } private function stubExecuteStreamingSql($transactionOptions = self::BEGIN_RW_OPTIONS) { $sql = $this->createStreamingAPIArgs()['sql']; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', $transactionOptions), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true, self::TRANSACTION)); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql, $transactionOptions) { + return $request->getSql() == $sql + && $request->getTransaction() == $this->serializer->decodeMessage( + new TransactionSelector, + $transactionOptions + ) + && $request->getRequestOptions()->getTransactionTag() == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn($this->resultGeneratorStream( + null, + new ResultSetStats(['row_count_exact' => 1]), + self::TRANSACTION + )); } private function stubExecuteBatchDml($transactionOptions = self::BEGIN_RW_OPTIONS) { - $this->connection->executeBatchDml(Argument::allOf( - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG - ]), - Argument::withEntry('transaction', $transactionOptions), - ))->shouldBeCalled()->willReturn([ - 'resultSets' => [['metadata' => ['transaction' => ['id' => self::TRANSACTION]]]] - ]); + $this->spannerClient->executeBatchDml( + Argument::that(function (ExecuteBatchDmlRequest $request) use ($transactionOptions) { + $this->assertEquals( + $request->getTransaction(), + $this->serializer->decodeMessage(new TransactionSelector, $transactionOptions) + ); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteBatchDmlResponse([ + 'result_sets' => [ + new ResultSet(['metadata' => new ResultSetMetadata([ + 'transaction' => new TransactionProto(['id' => self::TRANSACTION]) + ])]) + ] + ])); } } diff --git a/Spanner/tests/Unit/DurationTest.php b/Spanner/tests/Unit/DurationTest.php deleted file mode 100644 index 22eb0495f947..000000000000 --- a/Spanner/tests/Unit/DurationTest.php +++ /dev/null @@ -1,66 +0,0 @@ -duration = new Duration(self::SECONDS, self::NANOS); - } - - public function testGet() - { - $this->assertEquals([ - 'seconds' => self::SECONDS, - 'nanos' => self::NANOS - ], $this->duration->get()); - } - - public function testType() - { - $this->assertEquals(Duration::TYPE, $this->duration->type()); - } - - public function testFormatAsString() - { - $this->assertEquals( - json_encode($this->duration->get()), - $this->duration->formatAsString() - ); - } - - public function testTostring() - { - $this->assertEquals( - json_encode($this->duration->get()), - (string)$this->duration - ); - } -} diff --git a/Spanner/tests/Unit/Fixtures.php b/Spanner/tests/Unit/Fixtures.php index 44a9d28eb2bb..20b74b87b727 100644 --- a/Spanner/tests/Unit/Fixtures.php +++ b/Spanner/tests/Unit/Fixtures.php @@ -25,11 +25,6 @@ public static function STREAMING_READ_ACCEPTANCE_FIXTURE() return __DIR__ . '/fixtures/streaming-read-acceptance-test.json'; } - public static function INSTANCE_FIXTURE() - { - return __DIR__ . '/fixtures/instance.json'; - } - public static function INSTANCE_CONFIG_FIXTURE() { return __DIR__ . '/fixtures/instanceConfig.json'; diff --git a/Spanner/tests/Unit/GapicBackoff/GapicBackoffTest.php b/Spanner/tests/Unit/GapicBackoff/GapicBackoffTest.php deleted file mode 100644 index 1d22aa87a908..000000000000 --- a/Spanner/tests/Unit/GapicBackoff/GapicBackoffTest.php +++ /dev/null @@ -1,114 +0,0 @@ -checkAndSkipGrpcTests(); - } - - /** - * @param array $config - * @return \Google\Cloud\Core\GrpcRequestWrapper - */ - private function getWrapper($config) - { - $config += [ - 'apiEndpoint' => '127.0.0.1:10', - 'hasEmulator' => true, - ]; - - $spanner = new SpannerClient($config); - $connection = $spanner->instance('nonexistent')->database('nonexistent')->connection(); - return $connection->requestWrapper(); - } - - public function provideDisabledBackoffConfigs() - { - return [ - [[]], - [['useDiscreteBackoffs' => false]], - ]; - } - - /** - * @dataProvider provideDisabledBackoffConfigs - * @param array $config - */ - public function testBackoffDisabledByDefault($config) - { - $wrapper = $this->getWrapper($config); - $handler = function () { - $args = func_get_args(); - return new MockOperationResponse('test', null, array_pop($args)); - }; - $response = $wrapper->send($handler, [[]]); - $expected = [ - 'retriesEnabled' => false, - ]; - $this->assertEquals($expected, $response->options['retrySettings']); - } - - public function testBackoffEnabledManually() - { - $config = [ - 'useDiscreteBackoffs' => true, - ]; - $wrapper = $this->getWrapper($config); - $handler = function () { - $args = func_get_args(); - return new MockOperationResponse('test', null, array_pop($args)); - }; - $response = $wrapper->send($handler, [[]]); - $expected = []; - $this->assertEquals($expected, $response->options['retrySettings']); - } - - public function testUserConfigsAreNotRuined() - { - $retrySettings = [ - 'retriesEnabled' => false, - 'noRetriesRpcTimeoutMillis' => 1234, - ]; - $config = [ - 'useDiscreteBackoffs' => true, - 'grpcOptions' => [ - 'retrySettings' => $retrySettings, - ], - ]; - $wrapper = $this->getWrapper($config); - $handler = function () { - $args = func_get_args(); - return new MockOperationResponse('test', null, array_pop($args)); - }; - $response = $wrapper->send($handler, [[]]); - $this->assertEquals($retrySettings, $response->options['retrySettings']); - } -} diff --git a/Spanner/tests/Unit/GapicBackoff/MockOperationResponse.php b/Spanner/tests/Unit/GapicBackoff/MockOperationResponse.php deleted file mode 100644 index 156428c89863..000000000000 --- a/Spanner/tests/Unit/GapicBackoff/MockOperationResponse.php +++ /dev/null @@ -1,31 +0,0 @@ -options = $options; - parent::__construct($operationName, $operationsClient, $options); - } -} diff --git a/Spanner/tests/Unit/InstanceConfigurationTest.php b/Spanner/tests/Unit/InstanceConfigurationTest.php index 944e8bf99291..8ec031e493a0 100644 --- a/Spanner/tests/Unit/InstanceConfigurationTest.php +++ b/Spanner/tests/Unit/InstanceConfigurationTest.php @@ -17,13 +17,20 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Core\Exception\NotFoundException; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\ApiException; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; +use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; use Google\Cloud\Spanner\InstanceConfiguration; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\LongRunning\Operation; +use Google\Protobuf\Any; +use Google\Rpc\Code; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -36,183 +43,231 @@ class InstanceConfigurationTest extends TestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT_ID = 'test-project'; const NAME = 'test-config'; - private $connection; - private $configuration; + private $instanceAdminClient; + private Serializer $serializer; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); - $this->configuration = TestHelpers::stub(InstanceConfiguration::class, [ - $this->connection->reveal(), - self::PROJECT_ID, - self::NAME, - [], - $this->prophesize(LongRunningConnectionInterface::class)->reveal() - ]); + $this->instanceAdminClient = $this->prophesize(InstanceAdminClient::class); + $this->serializer = new Serializer(); } public function testName() { + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); + $this->assertEquals( - InstanceAdminClient::parseName($this->configuration->name())['instance_config'], + InstanceAdminClient::parseName($instanceConfig->name())['instance_config'], self::NAME ); } public function testInfo() { - $this->connection->getInstanceConfig(Argument::any())->shouldNotBeCalled(); - $this->configuration->___setProperty('connection', $this->connection->reveal()); - $info = ['foo' => 'bar']; - $config = TestHelpers::stub(InstanceConfiguration::class, [ - $this->connection->reveal(), + $this->instanceAdminClient->getInstanceConfig(Argument::cetera()) + ->shouldNotBeCalled(); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, self::PROJECT_ID, self::NAME, - $info, - $this->prophesize(LongRunningConnectionInterface::class)->reveal() - ]); + $info + ); - $this->assertEquals($info, $config->info()); + $this->assertEquals($info, $instanceConfig->info()); } public function testInfoWithReload() { - $info = ['foo' => 'bar']; - - $this->connection->getInstanceConfig([ - 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT_ID, self::NAME), - 'projectName' => InstanceAdminClient::projectName(self::PROJECT_ID) - ])->shouldBeCalled()->willReturn($info); - - $this->configuration->___setProperty('connection', $this->connection->reveal()); + $expected = ['display_name' => 'foo']; + $this->instanceAdminClient->getInstanceConfig( + Argument::type(GetInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceConfig($expected)); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); + $info = $instanceConfig->info(); - $this->assertEquals($info, $this->configuration->info()); + $this->assertArrayHasKey('displayName', $info); + $this->assertEquals($expected['display_name'], $info['displayName']); } public function testExists() { - $this->connection->getInstanceConfig(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->configuration->name()) - )) - ->shouldBeCalled() - ->willReturn([]); - $this->configuration->___setProperty('connection', $this->connection->reveal()); - - $this->assertTrue($this->configuration->exists()); + $this->instanceAdminClient->getInstanceConfig( + Argument::type(GetInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceConfig()); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); + $this->assertTrue($instanceConfig->exists()); } public function testExistsDoesntExist() { - $this->connection->getInstanceConfig(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->configuration->name()) - )) - ->shouldBeCalled() - ->willThrow(new NotFoundException('', 404)); - $this->configuration->___setProperty('connection', $this->connection->reveal()); - - $this->assertFalse($this->configuration->exists()); + $this->instanceAdminClient->getInstanceConfig( + Argument::type(GetInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->will(function() { + throw new ApiException('', Code::NOT_FOUND); + }); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); + + $this->assertFalse($instanceConfig->exists()); } public function testReload() { - $info = ['foo' => 'bar']; - - $this->connection->getInstanceConfig([ - 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT_ID, self::NAME), - 'projectName' => InstanceAdminClient::projectName(self::PROJECT_ID) - ])->shouldBeCalledTimes(1)->willReturn($info); - - $this->configuration->___setProperty('connection', $this->connection->reveal()); - - $info = $this->configuration->reload(); + $expected1 = ['some' => 'info']; + $expected2 = ['display_name' => 'bar']; + $this->instanceAdminClient->getInstanceConfig( + Argument::type(GetInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceConfig($expected2)); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME, + $expected1 + ); - $info2 = $this->configuration->info(); + $info1 = $instanceConfig->info(); + $info2 = $instanceConfig->reload(); + $info3 = $instanceConfig->info(); - $this->assertEquals($info, $info2); + $this->assertEquals($expected1, $info1); + $this->assertNotEquals($info1, $info2); + $this->assertArrayHasKey('displayName', $info2); + $this->assertEquals($expected2['display_name'], $info2['displayName']); + $this->assertEquals($info2, $info3); } public function testUpdate() { - $config = $this->getDefaultInstance(); - - $this->connection->updateInstanceConfig([ - 'name' => $config['name'], - 'displayName' => 'bar', - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' + $expectedInstanceConfig = new InstanceConfig([ + 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT_ID, 'foo'), + 'display_name' => 'bar2' ]); - - $this->configuration->___setProperty('connection', $this->connection->reveal()); - - $this->configuration->update(['displayName' => 'bar']); - } - - public function testUpdateWithExistingLabels() - { - $config = $this->getDefaultInstance(); - $config['labels'] = ['foo' => 'bar']; - - $this->connection->updateInstanceConfig([ - 'labels' => $config['labels'], - 'name' => $config['name'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' + $any = $this->prophesize(Any::class); + $any->getValue()->willReturn($expectedInstanceConfig->serializeToString()); + $operation = $this->prophesize(Operation::class); + $operation->getResponse()->willReturn($any->reveal()); + $operation->getDone()->willReturn(true); + $operationClient = $this->prophesize(\Google\LongRunning\Client\OperationsClient::class); + $operationResponse = new OperationResponse('operation-name', $operationClient->reveal(), [ + 'operationReturnType' => InstanceConfig::class, + 'lastProtoResponse' => $operation->reveal(), ]); - $this->configuration->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->updateInstanceConfig( + Argument::that(function (UpdateInstanceConfigRequest $request) use ($expectedInstanceConfig) { + $instanceConfig = $request->getInstanceConfig(); + return $instanceConfig->getDisplayName() === $expectedInstanceConfig->getDisplayName() + && $instanceConfig->getName() === $expectedInstanceConfig->getName(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($operationResponse); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + 'foo', + ); + + $operation = $instanceConfig->update(['displayName' => 'bar2']); + $operation->pollUntilComplete(); + $updatedInstanceConfig = $operation->getResult(); - $this->configuration->update(['labels' => $config['labels']]); + $info = $updatedInstanceConfig->info(); + $this->assertEquals('bar2', $info['displayName']); } public function testUpdateWithChanges() { - $config = $this->getDefaultInstance(); - - $changes = [ - 'labels' => [ - 'foo' => 'bar' - ], + $config = [ + 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT_ID, self::NAME), + 'labels' => ['foo' => 'bar'], 'displayName' => 'New Name', ]; - $this->connection->updateInstanceConfig([ - 'name' => $config['name'], - 'displayName' => $changes['displayName'], - 'labels' => $changes['labels'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); - - $this->configuration->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->updateInstanceConfig( + Argument::that(function (UpdateInstanceConfigRequest $request) use ($config) { + $instanceConfig = $request->getInstanceConfig()->serializeToJsonString(); + return json_decode($instanceConfig, true) == $config; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); - $this->configuration->update($changes); + $instanceConfig->update(['displayName' => 'New Name', 'labels' => ['foo' => 'bar']]); } public function testDelete() { - $this->connection->deleteInstanceConfig([ - 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT_ID, self::NAME) - ])->shouldBeCalled(); - - $this->configuration->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->deleteInstanceConfig( + Argument::type(DeleteInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce(); + + $instanceConfig = new InstanceConfiguration( + $this->instanceAdminClient->reveal(), + $this->serializer, + self::PROJECT_ID, + self::NAME + ); - $this->configuration->delete(); + $instanceConfig->delete(); } private function getDefaultInstance() diff --git a/Spanner/tests/Unit/InstanceTest.php b/Spanner/tests/Unit/InstanceTest.php index cb0564d92e0e..d821ee9d04bf 100644 --- a/Spanner/tests/Unit/InstanceTest.php +++ b/Spanner/tests/Unit/InstanceTest.php @@ -17,18 +17,34 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\ApiCore\Serializer; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\PagedListResponse; +use Google\ApiCore\Page; +use Google\LongRunning\Operation; use Google\Cloud\Core\Exception\NotFoundException; -use Google\Cloud\Core\Iam\Iam; +use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; -use Google\Cloud\Core\LongRunning\LongRunningOperation; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\LongRunning\Client\OperationsClient; +use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsResponse; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesResponse; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsResponse; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; -use Google\Cloud\Spanner\Tests\StubCreationTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type; +use Google\Cloud\Spanner\V1\Session; +use Google\Cloud\Spanner\V1\CreateSessionRequest; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; use Google\Cloud\Spanner\Backup; use InvalidArgumentException; use PHPUnit\Framework\TestCase; @@ -47,8 +63,6 @@ class InstanceTest extends TestCase use GrpcTestTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; - use ResultGeneratorTrait; const PROJECT_ID = 'test-project'; const NAME = 'instance-name'; @@ -56,38 +70,51 @@ class InstanceTest extends TestCase const BACKUP = 'my-backup'; const SESSION = 'projects/test-project/instances/instance-name/databases/database-name/sessions/session'; - private $connection; - private $instance; - private $lroConnection; private $directedReadOptionsIncludeReplicas; + private $instance; + private $serializer; + private $spannerClient; + private $instanceAdminClient; + private $databaseAdminClient; + private $operationResponse; + private $page; + private $pagedListResponse; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); $this->directedReadOptionsIncludeReplicas = [ 'includeReplicas' => [ - 'replicaSelections' => [ + 'replicaSelections' => [[ 'location' => 'us-central1', - 'type' => 'READ_WRITE', - 'autoFailoverDisabled' => false - ] + 'type' => Type::READ_WRITE, + ]], 'autoFailoverDisabled' => false ] ]; - $this->instance = TestHelpers::stub(Instance::class, [ - $this->connection->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], + $this->spannerClient = $this->prophesize(SpannerClient::class); + $this->instanceAdminClient = $this->prophesize(InstanceAdminClient::class); + $this->databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $this->operationResponse = $this->prophesize(OperationResponse::class); + $this->operationResponse->withResultFunction(Argument::type('callable')) + ->willReturn($this->operationResponse->reveal()); + + $this->page = $this->prophesize(Page::class); + $this->pagedListResponse = $this->prophesize(PagedListResponse::class); + $this->pagedListResponse->getPage()->willReturn($this->page->reveal()); + + $this->instance = new Instance( + $this->spannerClient->reveal(), + $this->instanceAdminClient->reveal(), + $this->databaseAdminClient->reveal(), + $this->serializer, self::PROJECT_ID, self::NAME, false, [], ['directedReadOptions' => $this->directedReadOptionsIncludeReplicas] - ], [ - 'info', - 'connection' - ]); + ); } public function testName() @@ -97,50 +124,39 @@ public function testName() public function testInfo() { - $this->connection->getInstance(Argument::any())->shouldNotBeCalled(); - - $this->instance->___setProperty('info', ['foo' => 'bar']); - $this->assertEquals('bar', $this->instance->info()['foo']); - } - - public function testInfoWithReload() - { - $instance = $this->getDefaultInstance(); - - $this->connection->getInstance(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->instance->name()) - )) - ->shouldBeCalledTimes(1) - ->willReturn($instance); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + return $message['name'] == $this->instance->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->getDefaultInstance()); $info = $this->instance->info(); $this->assertEquals('Instance Name', $info['displayName']); + // test calling info again does not reload $this->assertEquals($info, $this->instance->info()); } public function testInfoWithReloadAndFieldMask() { - $instance = [ - 'name' => $this->instance->name(), - 'node_count' => 1 - ]; - $requestedFieldNames = ["name", 'node_count']; - $this->connection->getInstance(Argument::allOf( - Argument::withEntry('name', $this->instance->name()), - Argument::withEntry('fieldMask', $requestedFieldNames) - )) - ->shouldBeCalledTimes(1) - ->willReturn($instance); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) use ($requestedFieldNames) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals(['paths' => $requestedFieldNames], $message['fieldMask']); + return $message['name'] == $this->instance->name(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceProto([ + 'name' => $this->instance->name(), + 'node_count' => 1 + ])); $info = $this->instance->info(['fieldMask' => $requestedFieldNames]); @@ -149,32 +165,30 @@ public function testInfoWithReloadAndFieldMask() public function testExists() { - $this->connection->getInstance(Argument::allOf( - Argument::withEntry('name', $this->instance->name()), - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('fieldMask', ['name']) - )) - ->shouldBeCalledTimes(1) - ->willReturn([]); - - $this->connection->getInstance(Argument::allOf( - Argument::withEntry('name', $this->instance->name()), - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::not(Argument::withKey('fieldMask')) - )) + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return isset($message['fieldMask']) && ['paths' => ['name']] == $message['fieldMask']; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceProto()); + + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return !isset($message['fieldMask']); + }), + Argument::type('array') + ) ->shouldBeCalledTimes(2) - ->willReturn([ + ->willReturn(new InstanceProto([ 'name' => $this->instance->name(), - 'nodeCount' => 1, - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + 'node_count' => 1, + ])); $this->assertTrue($this->instance->exists()); @@ -185,36 +199,32 @@ public function testExists() public function testExistsNotFound() { - $this->connection->getInstance(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->instance->name()) - )) - ->shouldBeCalled() + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() ->willThrow(new NotFoundException('foo', 404)); - $this->instance->___setProperty('connection', $this->connection->reveal()); - $this->assertFalse($this->instance->exists()); } public function testReload() { - $instance = $this->getDefaultInstance(); - - $this->connection->getInstance(Argument::allOf( - Argument::withEntry('name', $this->instance->name()), - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ) - )) - ->shouldBeCalledTimes(1) - ->willReturn($instance); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->getDefaultInstance()); $info = $this->instance->reload(); @@ -223,24 +233,20 @@ public function testReload() public function testReloadWithFieldMask() { - $instance = [ - 'name' => $this->instance->name(), - 'node_count' => 1 - ]; - $requestedFieldNames = ["name", 'node_count']; - $this->connection->getInstance(Argument::allOf( - Argument::withEntry('name', $this->instance->name()), - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('fieldMask', $requestedFieldNames) - )) - ->shouldBeCalledTimes(1) - ->willReturn($instance); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) use ($requestedFieldNames) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return $message['fieldMask'] == ['paths' => $requestedFieldNames]; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceProto([ + 'name' => $this->instance->name(), + 'node_count' => 1 + ])); $info = $this->instance->reload(['fieldMask' => $requestedFieldNames]); @@ -249,68 +255,65 @@ public function testReloadWithFieldMask() public function testState() { - $instance = $this->getDefaultInstance(); - - $this->connection->getInstance(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->instance->name()) - )) - ->shouldBeCalledTimes(1) - ->willReturn($instance); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->getDefaultInstance()); $this->assertEquals(Instance::STATE_READY, $this->instance->state()); } public function testStateIsNull() { - $this->connection->getInstance(Argument::allOf( - Argument::withEntry( - 'projectName', - InstanceAdminClient::projectName(self::PROJECT_ID) - ), - Argument::withEntry('name', $this->instance->name()) - )) - ->shouldBeCalledTimes(1) - ->willReturn([]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->getInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['name'], $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new InstanceProto()); $this->assertNull($this->instance->state()); } public function testUpdate() { - $instance = $this->getDefaultInstance(); - - $this->connection->updateInstance([ - 'displayName' => 'bar', - 'name' => $instance['name'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->updateInstance( + Argument::that(function ($request) { + $this->assertEquals('bar', $request->getInstance()->getDisplayName()); + $this->assertEquals($this->instance->name(), $request->getInstance()->getName()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $this->instance->update(['displayName' => 'bar']); } public function testUpdateWithProcessingUnits() { - $instance = $this->getDefaultInstance(); - - $this->connection->updateInstance([ - 'processingUnits' => 500, - 'name' => $instance['name'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->updateInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals(500, $request->getInstance()->getProcessingUnits()); + $this->assertEquals($this->instance->name(), $request->getInstance()->getName()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $this->instance->update(['processingUnits' => 500]); } @@ -324,54 +327,55 @@ public function testUpdateRaisesInvalidArgument() public function testUpdateWithExistingLabels() { - $instance = $this->getDefaultInstance(); - $instance['labels'] = ['foo' => 'bar']; - - $this->connection->updateInstance([ - 'labels' => $instance['labels'], - 'name' => $instance['name'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' - ]); + $this->instanceAdminClient->updateInstance( + Argument::that(function ($request) { + $this->assertEquals(['foo' => 'bar'], iterator_to_array($request->getInstance()->getLabels())); + $this->assertEquals($this->instance->name(), $request->getInstance()->getName()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); - $this->instance->___setProperty('connection', $this->connection->reveal()); - - $this->instance->update(['labels' => $instance['labels']]); + $this->instance->update(['labels' => ['foo' => 'bar']]); } public function testUpdateWithChanges() { - $instance = $this->getDefaultInstance(); - - $changes = [ + $this->instanceAdminClient->updateInstance( + Argument::that(function ($request) { + $this->assertEquals('New Name', $request->getInstance()->getDisplayName()); + $this->assertEquals(['foo' => 'bar'], iterator_to_array($request->getInstance()->getLabels())); + $this->assertEquals(900, $request->getInstance()->getNodeCount()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); + + $this->instance->update([ 'labels' => [ 'foo' => 'bar' ], 'nodeCount' => 900, 'displayName' => 'New Name', - ]; - - $this->connection->updateInstance([ - 'name' => $instance['name'], - 'displayName' => $changes['displayName'], - 'nodeCount' => $changes['nodeCount'], - 'labels' => $changes['labels'], - ])->shouldBeCalled()->willReturn([ - 'name' => 'my-operation' ]); - - $this->instance->___setProperty('connection', $this->connection->reveal()); - - $this->instance->update($changes); } public function testDelete() { - $this->connection->deleteInstance([ - 'name' => InstanceAdminClient::instanceName(self::PROJECT_ID, self::NAME) - ])->shouldBeCalled(); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->instanceAdminClient->deleteInstance( + Argument::that(function ($request) { + $this->assertEquals( + $request->getName(), + InstanceAdminClient::instanceName(self::PROJECT_ID, self::NAME) + ); + return true; + }), + Argument::type('array') + )->shouldBeCalledOnce(); $this->instance->delete(); } @@ -380,57 +384,68 @@ public function testCreateDatabase() { $extra = ['foo', 'bar']; - $this->connection->createDatabase([ - 'instance' => InstanceAdminClient::instanceName(self::PROJECT_ID, self::NAME), - 'createStatement' => 'CREATE DATABASE `test-database`', - 'extraStatements' => $extra - ]) - ->shouldBeCalled() - ->willReturn(['name' => 'operations/foo']); - - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->createDatabase( + Argument::that(function ($request) use ($extra) { + $createStatement = 'CREATE DATABASE `test-database`'; + $message = $this->serializer->encodeMessage($request); + $this->assertEquals($message['createStatement'], $createStatement); + $this->assertEquals( + $message['parent'], + InstanceAdminClient::instanceName(self::PROJECT_ID, self::NAME) + ); + $this->assertEquals($message['extraStatements'], $extra); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $database = $this->instance->createDatabase('test-database', [ 'statements' => $extra ]); - $this->assertInstanceOf(LongRunningOperation::class, $database); + $this->assertInstanceOf(OperationResponse::class, $database); } public function testCreateDatabaseFromBackupName() { $backupName = DatabaseAdminClient::backupName(self::PROJECT_ID, self::NAME, self::BACKUP); - $this->connection->restoreDatabase(Argument::allOf( - Argument::withEntry('databaseId', 'restore-database'), - Argument::withEntry('instance', $this->instance->name()), - Argument::withEntry('backup', $backupName) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - $this->instance->___setProperty('connection', $this->connection->reveal()); + + $this->databaseAdminClient->restoreDatabase( + Argument::that(function ($request) use ($backupName) { + $this->assertEquals($request->getDatabaseId(), 'restore-database'); + $this->assertEquals($request->getParent(), $this->instance->name()); + $this->assertEquals($request->getBackup(), $backupName); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->instance->createDatabaseFromBackup('restore-database', $backupName); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } public function testCreateDatabaseFromBackupObject() { $backupObject = $this->instance->backup(self::BACKUP); - $this->connection->restoreDatabase(Argument::allOf( - Argument::withEntry('databaseId', 'restore-database'), - Argument::withEntry('instance', $this->instance->name()), - Argument::withEntry('backup', $backupObject->name()) - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => 'my-operation' - ]); - $this->instance->___setProperty('connection', $this->connection->reveal()); + + $this->databaseAdminClient->restoreDatabase( + Argument::that(function ($request) use ($backupObject) { + $this->assertEquals($request->getDatabaseId(), 'restore-database'); + $this->assertEquals($request->getParent(), $this->instance->name()); + $this->assertEquals($request->getBackup(), $backupObject->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->operationResponse->reveal()); $op = $this->instance->createDatabaseFromBackup('restore-database', $backupObject); - $this->assertInstanceOf(LongRunningOperation::class, $op); + $this->assertInstanceOf(OperationResponse::class, $op); } public function testDatabase() @@ -443,17 +458,23 @@ public function testDatabase() public function testDatabases() { $databases = [ - ['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database1')], - ['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database2')] + new DatabaseProto(['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database1')]), + new DatabaseProto(['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database2')]) ]; - $this->connection->listDatabases(Argument::withEntry('instance', $this->instance->name())) - ->shouldBeCalled() - ->willReturn(['databases' => $databases]); + $this->page->getResponseObject()->willReturn(new ListDatabasesResponse(['databases' => $databases])); - $this->connection->getDatabase(Argument::any())->shouldNotBeCalled(); + $this->databaseAdminClient->listDatabases( + Argument::that(function ($request) { + $this->assertEquals($request->getParent(), $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->pagedListResponse->reveal()); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->getDatabase(Argument::cetera())->shouldNotBeCalled(); $dbs = $this->instance->databases(); @@ -466,23 +487,59 @@ public function testDatabases() $this->assertEquals('database2', DatabaseAdminClient::parseName($dbs[1]->name())['database']); // Make sure the database->info is prefilled. - $this->assertEquals($databases[0], $dbs[0]->info()); - $this->assertEquals($databases[1], $dbs[1]->info()); + $this->assertEquals($databases[0]->__debugInfo(), array_filter($dbs[0]->info())); + $this->assertEquals($databases[1]->__debugInfo(), array_filter($dbs[1]->info())); } public function testDatabasesPaged() { $databases = [ - ['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database1')], - ['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database2')] + new DatabaseProto(['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database1')]), + new DatabaseProto(['name' => DatabaseAdminClient::databaseName(self::PROJECT_ID, self::NAME, 'database2')]), ]; - $iteration = 0; - $this->connection->listDatabases(Argument::withEntry('instance', $this->instance->name())) - ->shouldBeCalledTimes(2) - ->willReturn(['databases' => [$databases[0]], 'nextPageToken' => 'foo'], ['databases' => [$databases[1]]]); + $page1 = $this->prophesize(Page::class); + $page1->getResponseObject() + ->willReturn(new ListDatabasesResponse([ + 'databases' => [$databases[0]], 'next_page_token' => 'foo'] + )); + $pagedListResponse1 = $this->prophesize(PagedListResponse::class); + $pagedListResponse1->getPage() + ->willReturn($page1->reveal()); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $iteration = 0; + $this->databaseAdminClient->listDatabases( + Argument::that(function ($request) use (&$iteration) { + $this->assertEquals($request->getParent(), $this->instance->name()); + $iteration++; + return $iteration == 1; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($pagedListResponse1->reveal()); + + $page2 = $this->prophesize(Page::class); + $page2->getResponseObject() + ->willReturn(new ListDatabasesResponse(['databases' => [$databases[1]]])); + $pagedListResponse2 = $this->prophesize(PagedListResponse::class); + $pagedListResponse2->getPage() + ->willReturn($page2->reveal()); + + $this->databaseAdminClient->listDatabases( + Argument::that(function ($request) use (&$iteration) { + $this->assertEquals($request->getParent(), $this->instance->name()); + if ($iteration == 2) { + $this->assertEquals($request->getPageToken(), 'foo'); + return true; + } + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($pagedListResponse2->reveal()); + + $this->databaseAdminClient->getDatabase(Argument::cetera())->shouldNotBeCalled(); $dbs = $this->instance->databases(); @@ -497,7 +554,7 @@ public function testDatabasesPaged() public function testIam() { - $this->assertInstanceOf(Iam::class, $this->instance->iam()); + $this->assertInstanceOf(IamManager::class, $this->instance->iam()); } public function testBackup() @@ -513,19 +570,21 @@ public function testBackup() public function testBackups() { $backups = [ - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::NAME, 'backup1'), - ], - [ - 'name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::NAME, 'backup2'), - ] + new BackupProto(['name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::NAME, 'backup1')]), + new BackupProto(['name' => DatabaseAdminClient::backupName(self::PROJECT_ID, self::NAME, 'backup2')]), ]; - $this->connection->listBackups(Argument::withEntry('instance', $this->instance->name())) - ->shouldBeCalled() - ->willReturn(['backups' => $backups]); + $this->page->getResponseObject()->willReturn(new ListBackupsResponse(['backups' => $backups])); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->listBackups( + Argument::that(function ($request) { + $this->assertEquals($request->getParent(), $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->pagedListResponse->reveal()); $bkps = $this->instance->backups(); @@ -541,15 +600,25 @@ public function testBackups() public function testBackupOperations() { $operations = [ - ['name' => 'operation1'], - ['name' => 'operation2'] + new Operation(['name' => 'operation1']), + new Operation(['name' => 'operation2']), ]; - $this->connection->listBackupOperations(Argument::withEntry('instance', $this->instance->name())) - ->shouldBeCalled() - ->willReturn(['operations' => $operations]); + $this->page->getResponseObject()->willReturn(new ListBackupOperationsResponse(['operations' => $operations])); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->listBackupOperations( + Argument::that(function ($request) { + $this->assertEquals($request->getParent(), $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->pagedListResponse->reveal()); + + $this->databaseAdminClient->getOperationsClient() + ->shouldBeCalledTimes(2) + ->willReturn($this->prophesize(OperationsClient::class)->reveal()); $bkpOps = $this->instance->backupOperations(); @@ -557,22 +626,32 @@ public function testBackupOperations() $bkpOps = iterator_to_array($bkpOps); $this->assertCount(2, $bkpOps); - $this->assertEquals('operation1', $bkpOps[0]->name()); - $this->assertEquals('operation2', $bkpOps[1]->name()); + $this->assertEquals('operation1', $bkpOps[0]->getName()); + $this->assertEquals('operation2', $bkpOps[1]->getName()); } public function testListDatabaseOperations() { $operations = [ - ['name' => 'operation1'], - ['name' => 'operation2'] + new Operation(['name' => 'operation1']), + new Operation(['name' => 'operation2']), ]; - $this->connection->listDatabaseOperations(Argument::withEntry('instance', $this->instance->name())) - ->shouldBeCalled() - ->willReturn(['operations' => $operations]); + $this->page->getResponseObject()->willReturn(new ListDatabaseOperationsResponse(['operations' => $operations])); - $this->instance->___setProperty('connection', $this->connection->reveal()); + $this->databaseAdminClient->listDatabaseOperations( + Argument::that(function ($request) { + $this->assertEquals($request->getParent(), $this->instance->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->pagedListResponse->reveal()); + + $this->databaseAdminClient->getOperationsClient() + ->shouldBeCalledTimes(2) + ->willReturn($this->prophesize(OperationsClient::class)->reveal()); $dbOps = $this->instance->databaseOperations(); @@ -580,8 +659,8 @@ public function testListDatabaseOperations() $dbOps = iterator_to_array($dbOps); $this->assertCount(2, $dbOps); - $this->assertEquals('operation1', $dbOps[0]->name()); - $this->assertEquals('operation2', $dbOps[1]->name()); + $this->assertEquals('operation1', $dbOps[0]->getName()); + $this->assertEquals('operation2', $dbOps[1]->getName()); } public function testInstanceDatabaseRole() @@ -589,16 +668,29 @@ public function testInstanceDatabaseRole() $sql = 'SELECT * FROM Table'; $database = $this->instance->database($this::DATABASE, ['databaseRole' => 'Reader']); - $this->connection->createSession(Argument::withEntry( - 'session', - ['labels' => [], 'creator_role' => 'Reader'] - )) - ->shouldBeCalled() - ->willReturn([ - 'name' => self::SESSION - ]); - $this->connection->executeStreamingSql(Argument::withEntry('sql', $sql)) - ->shouldBeCalled()->willReturn($this->resultGenerator()); + $this->spannerClient->createSession( + Argument::that(function ($request) { + return $this->serializer->encodeMessage($request)['session']['creatorRole'] + == 'Reader'; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Session(['name' => self::SESSION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + return $request->getSql() == $sql; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); + + $this->spannerClient->deleteSession( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') + )->shouldBeCalledOnce(); $database->execute($sql); } @@ -608,20 +700,31 @@ public function testInstanceExecuteWithDirectedRead() $database = $this->instance->database( $this::DATABASE ); - $this->connection->createSession(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => self::SESSION - ]); - - $this->connection->executeStreamingSql(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsIncludeReplicas - )) - ->shouldBeCalled() - ->willReturn( - $this->resultGenerator() - ); + $this->spannerClient->createSession( + Argument::type(CreateSessionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Session(['name' => self::SESSION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsIncludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); + + $this->spannerClient->deleteSession( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') + )->shouldBeCalledOnce(); $sql = 'SELECT * FROM Table'; $res = $database->execute($sql); @@ -635,23 +738,33 @@ public function testInstanceReadWithDirectedRead() $table = 'foo'; $keys = [10, 'bar']; $columns = ['id', 'name']; - $database = $this->instance->database( - $this::DATABASE, - ); - $this->connection->createSession(Argument::any()) - ->shouldBeCalled() - ->willReturn([ - 'name' => self::SESSION - ]); - - $this->connection->streamingRead(Argument::withEntry( - 'directedReadOptions', - $this->directedReadOptionsIncludeReplicas - )) - ->shouldBeCalled() - ->willReturn( - $this->resultGenerator() - ); + $database = $this->instance->database($this::DATABASE); + + $this->spannerClient->createSession( + Argument::type(CreateSessionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Session(['name' => self::SESSION])); + + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['directedReadOptions'], + $this->directedReadOptionsIncludeReplicas + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); + + $this->spannerClient->deleteSession( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') + )->shouldBeCalledOnce(); $res = $database->read( $table, @@ -667,6 +780,12 @@ public function testInstanceReadWithDirectedRead() private function getDefaultInstance() { - return json_decode(file_get_contents(Fixtures::INSTANCE_FIXTURE()), true); + return new InstanceProto([ + 'name' => 'projects/test-project/instances/instance-name', + 'config' => 'projects/test-project/instanceConfigs/regional-europe-west1', + 'display_name' => 'Instance Name', + 'node_count' => 1, + 'state' => 2 + ]); } } diff --git a/Spanner/tests/Unit/OperationTest.php b/Spanner/tests/Unit/OperationTest.php index bfe2c23dfd4c..d4c932eaf47b 100644 --- a/Spanner/tests/Unit/OperationTest.php +++ b/Spanner/tests/Unit/OperationTest.php @@ -17,15 +17,15 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\Connection\Grpc; use Google\Cloud\Spanner\Database; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; @@ -33,18 +33,28 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\CommitResponse; +use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; +use Google\Cloud\Spanner\V1\PartialResultSet; +use Google\Cloud\Spanner\V1\Partition; +use Google\Cloud\Spanner\V1\PartitionResponse; +use Google\Cloud\Spanner\V1\ResultSetMetadata; +use Google\Cloud\Spanner\V1\StructType; +use Google\Cloud\Spanner\V1\StructType\Field; +use Google\Cloud\Spanner\V1\Transaction as TransactionProto; +use Google\Cloud\Spanner\V1\Type; +use Google\Protobuf\Value; +use Google\Protobuf\Duration; +use Google\Protobuf\Timestamp as TimestampProto; use Google\Cloud\Spanner\V1\ResultSet; use Google\Cloud\Spanner\V1\ResultSetStats; -use Google\Cloud\Spanner\V1\SpannerClient; -use Google\Cloud\Spanner\V1\Transaction as TransactionProto; use Google\Cloud\Spanner\V1\TransactionOptions; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; +use Prophecy\Argument; /** * @group spanner @@ -53,7 +63,7 @@ class OperationTest extends TestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; + use ApiHelperTrait; const SESSION = 'my-session-id'; const TRANSACTION = 'my-transaction-id'; @@ -61,20 +71,36 @@ class OperationTest extends TestCase const DATABASE = 'projects/my-awesome-project/instances/my-instance/databases/my-database'; const TIMESTAMP = '2017-01-09T18:05:22.534799Z'; - private $connection; private $operation; private $session; + private $spannerClient; + private $serializer; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]); + $this->spannerClient = $this->prophesize(SpannerClient::class); - $this->operation = TestHelpers::stub(Operation::class, [ - $this->connection->reveal(), + $this->operation = new Operation( + $this->spannerClient->reveal(), + $this->serializer, false - ]); + ); $session = $this->prophesize(Session::class); $session->name()->willReturn(self::SESSION); @@ -119,23 +145,29 @@ public function testDeleteMutation() public function testCommit() { - $mutations = [ - $this->operation->mutation(Operation::OP_INSERT, 'Posts', [ - 'foo' => 'bar' - ]) - ]; - - $this->connection->commit(Argument::allOf( - Argument::withEntry('mutations', $mutations), - Argument::withEntry('transactionId', 'foo') - ))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => self::TIMESTAMP - ]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $mutation = $this->operation->mutation(Operation::OP_INSERT, 'Posts', ['foo' => 'bar']); + + $this->spannerClient->commit( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getMutations()[0]->getInsert()->getTable()); + $this->assertEquals( + $this->serializer->encodeMessage($request->getMutations()[0]->getInsert())['values'], + [['bar']] + ); + $this->assertEquals( + $request->getMutations()[0]->getInsert()->getColumns()[0], + 'foo' + ); + $this->assertEquals(self::TRANSACTION, $request->getTransactionId()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); - $res = $this->operation->commit($this->session, $mutations, [ - 'transactionId' => 'foo' + $res = $this->operation->commit($this->session, [$mutation], [ + 'transactionId' => self::TRANSACTION ]); $this->assertInstanceOf(Timestamp::class, $res); @@ -143,24 +175,23 @@ public function testCommit() public function testCommitWithReturnCommitStats() { - $mutations = [ - $this->operation->mutation(Operation::OP_INSERT, 'Posts', [ - 'foo' => 'bar' - ]) - ]; + $mutation = $this->operation->mutation(Operation::OP_INSERT, 'Posts', ['foo' => 'bar']); - $this->connection->commit(Argument::allOf( - Argument::withEntry('mutations', $mutations), - Argument::withEntry('transactionId', 'foo'), - Argument::withEntry('returnCommitStats', true) - ))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => self::TIMESTAMP, - 'commitStats' => ['mutationCount' => 1] - ]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->commit( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getMutations()[0]->getInsert()->getTable()); + $this->assertEquals('foo', $request->getTransactionId()); + $this->assertEquals(true, $request->getReturnCommitStats()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse([ + 'commit_stats' => new CommitStats(['mutation_count' => 1]) + ])); - $res = $this->operation->commitWithResponse($this->session, $mutations, [ + $res = $this->operation->commitWithResponse($this->session, [$mutation], [ 'transactionId' => 'foo', 'returnCommitStats' => true ]); @@ -174,24 +205,29 @@ public function testCommitWithReturnCommitStats() public function testCommitWithMaxCommitDelay() { - $duration = new Duration(0, 100000000); - $mutations = [ - $this->operation->mutation(Operation::OP_INSERT, 'Posts', [ - 'foo' => 'bar' - ]) - ]; - - $this->connection->commit(Argument::allOf( - Argument::withEntry('mutations', $mutations), - Argument::withEntry('transactionId', 'foo'), - Argument::withEntry('maxCommitDelay', $duration) - ))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => self::TIMESTAMP, - ]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $duration = new Duration(['seconds' => 0, 'nanos' => 100000000]); + $mutation = $this->operation->mutation(Operation::OP_INSERT, 'Posts', ['foo' => 'bar']); + + $this->spannerClient->commit( + Argument::that(function ($request) use ($duration) { + $this->assertEquals('Posts', $request->getMutations()[0]->getInsert()->getTable()); + $this->assertEquals('foo', $request->getTransactionId()); + $this->assertEquals( + $duration->getSeconds(), + $request->getMaxCommitDelay()->getSeconds() + ); + $this->assertEquals( + $duration->getNanos(), + $request->getMaxCommitDelay()->getNanos() + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); - $res = $this->operation->commitWithResponse($this->session, $mutations, [ + $res = $this->operation->commitWithResponse($this->session, [$mutation], [ 'transactionId' => 'foo', 'maxCommitDelay' => $duration, ]); @@ -204,25 +240,20 @@ public function testCommitWithMaxCommitDelay() public function testCommitWithExistingTransaction() { - $mutations = [ - $this->operation->mutation(Operation::OP_INSERT, 'Posts', [ - 'foo' => 'bar' - ]) - ]; - - $this->connection->commit(Argument::allOf( - Argument::withEntry('mutations', $mutations), - Argument::withEntry('transactionId', self::TRANSACTION), - Argument::that(function ($arg) { - return !isset($arg['singleUseTransaction']); - }) - ))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => self::TIMESTAMP - ]); + $mutation = $this->operation->mutation(Operation::OP_INSERT, 'Posts', ['foo' => 'bar']); - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->commit( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getMutations()[0]->getInsert()->getTable()); + $this->assertEquals(self::TRANSACTION, $request->getTransactionId()); + return !$request->hasSingleUseTransaction(); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->commitResponse()); - $res = $this->operation->commit($this->session, $mutations, [ + $res = $this->operation->commit($this->session, [$mutation], [ 'transactionId' => self::TRANSACTION ]); @@ -231,12 +262,14 @@ public function testCommitWithExistingTransaction() public function testRollback() { - $this->connection->rollback(Argument::allOf( - Argument::withEntry('transactionId', self::TRANSACTION), - Argument::withEntry('session', self::SESSION) - ))->shouldBeCalled(); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->rollback( + Argument::that(function ($request) { + $this->assertEquals(self::TRANSACTION, $request->getTransactionId()); + $this->assertEquals(self::SESSION, $request->getSession()); + return true; + }), + Argument::type('array') + )->shouldBeCalledOnce(); $this->operation->rollback($this->session, self::TRANSACTION); } @@ -246,16 +279,22 @@ public function testExecute() $sql = 'SELECT * FROM Posts WHERE ID = @id'; $params = ['id' => 10]; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('params', ['id' => '10']), - Argument::that(function ($arg) { - return $arg['paramTypes']['id']['code'] === Database::TYPE_INT64; - }) - ))->shouldBeCalled()->willReturn($this->executeAndReadResponse()); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($sql) { + $data = $this->serializer->encodeMessage($request); + $this->assertEquals($sql, $request->getSql()); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertEquals(['id' => '10'], $data['params']); + $this->assertEquals( + ['id' => ['code' => Database::TYPE_INT64, 'typeAnnotation' => 0, 'protoTypeFqn' => '']], + $data['paramTypes'], + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->executeAndReadResponseStream()); $res = $this->operation->execute($this->session, $sql, [ 'parameters' => $params @@ -268,14 +307,18 @@ public function testExecute() public function testRead() { - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('table', 'Posts'), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('keySet', ['all' => true]), - Argument::withEntry('columns', ['foo']) - ))->shouldBeCalled()->willReturn($this->executeAndReadResponse()); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getTable()); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertTrue($request->getKeySet()->getAll()); + $this->assertEquals(['foo'], $this->serializer->encodeMessage($request)['columns']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->executeAndReadResponseStream()); $res = $this->operation->read($this->session, 'Posts', new KeySet(['all' => true]), ['foo']); $this->assertInstanceOf(Result::class, $res); @@ -285,20 +328,23 @@ public function testRead() public function testReadWithTransaction() { - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('table', 'Posts'), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('keySet', ['all' => true]), - Argument::withEntry('columns', ['foo']) - ))->shouldBeCalled()->willReturn($this->executeAndReadResponse([ - 'transaction' => ['id' => self::TRANSACTION] - ])); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getTable()); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertTrue($request->getKeySet()->getAll()); + $this->assertEquals(['foo'], $this->serializer->encodeMessage($request)['columns']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->executeAndReadResponseStream(self::TRANSACTION)); $res = $this->operation->read($this->session, 'Posts', new KeySet(['all' => true]), ['foo'], [ 'transactionContext' => SessionPoolInterface::CONTEXT_READWRITE ]); + $res->rows()->next(); $this->assertInstanceOf(Transaction::class, $res->transaction()); @@ -307,16 +353,18 @@ public function testReadWithTransaction() public function testReadWithSnapshot() { - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('table', 'Posts'), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('keySet', ['all' => true]), - Argument::withEntry('columns', ['foo']) - ))->shouldBeCalled()->willReturn($this->executeAndReadResponse([ - 'transaction' => ['id' => self::TRANSACTION] - ])); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->streamingRead( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getTable()); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertTrue($request->getKeySet()->getAll()); + $this->assertEquals(['foo'], $this->serializer->encodeMessage($request)['columns']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->executeAndReadResponseStream(self::TRANSACTION)); $res = $this->operation->read($this->session, 'Posts', new KeySet(['all' => true]), ['foo'], [ 'transactionContext' => SessionPoolInterface::CONTEXT_READ @@ -329,15 +377,15 @@ public function testReadWithSnapshot() public function testTransaction() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('session', $this->session->name()), - Argument::withEntry('requestOptions', ['transactionTag' => self::TRANSACTION_TAG]) - )) - ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $this->assertEquals($request->getSession(), $this->session->name()); + return $request->getRequestOptions()->getTransactionTag() == self::TRANSACTION_TAG; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $t = $this->operation->transaction($this->session, ['tag' => self::TRANSACTION_TAG]); $this->assertInstanceOf(Transaction::class, $t); @@ -346,15 +394,18 @@ public function testTransaction() public function testTransactionNoTag() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('session', $this->session->name()), - Argument::withEntry('requestOptions', []) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + $this->assertEquals($request->getSession(), $this->session->name()); + $this->assertEquals(0, $request->getRequestOptions()->getPriority()); + $this->assertEquals('', $request->getRequestOptions()->getRequestTag()); + $this->assertEquals('', $request->getRequestOptions()->getTransactionTag()); + return true; + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $t = $this->operation->transaction($this->session); $this->assertInstanceOf(Transaction::class, $t); @@ -423,14 +474,14 @@ public function testExecuteAndExecuteUpdateWithExcludeTxnFromChangeStreams() public function testSnapshot() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('session', $this->session->name()) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + return $request->getSession() == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $snap = $this->operation->snapshot($this->session); $this->assertInstanceOf(Snapshot::class, $snap); @@ -440,10 +491,7 @@ public function testSnapshot() public function testSnapshotSingleUse() { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotBeCalled(); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); $snap = $this->operation->snapshot($this->session, ['singleUse' => true]); $this->assertInstanceOf(Snapshot::class, $snap); @@ -453,14 +501,17 @@ public function testSnapshotSingleUse() public function testSnapshotWithTimestamp() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('database', self::DATABASE), - Argument::withEntry('session', $this->session->name()) - )) + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { + return $request->getSession() == $this->session->name(); + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn(['id' => self::TRANSACTION, 'readTimestamp' => self::TIMESTAMP]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + ->willReturn(new TransactionProto([ + 'id' => self::TRANSACTION, + 'read_timestamp' => new TimestampProto(['seconds' => (new \DateTime(self::TIMESTAMP))->format('U')]) + ])); $snap = $this->operation->snapshot($this->session); $this->assertInstanceOf(Snapshot::class, $snap); @@ -477,28 +528,24 @@ public function testPartitionQuery() $partitionToken1 = 'token1'; $partitionToken2 = 'token2'; - $this->connection->partitionQuery(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('params', ['id' => '10']), - Argument::that(function ($arg) use ($transactionId) { - if ($arg['paramTypes']['id']['code'] !== Database::TYPE_INT64) { - return false; - } - - return $arg['transactionId'] === $transactionId; - }) - ))->shouldBeCalled()->willReturn([ - 'partitions' => [ - [ - 'partitionToken' => $partitionToken1 - ], [ - 'partitionToken' => $partitionToken2 + $this->spannerClient->partitionQuery( + Argument::that(function ($request) use ($sql, $transactionId, $partitionToken1, $partitionToken2) { + $this->assertEquals($request->getSql(), $sql); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertEquals(['id' => '10'], $request->getParams()->__debugInfo()); + $this->assertEquals(Database::TYPE_INT64, $request->getParamTypes()['id']->getCode()); + $this->assertEquals($transactionId, $request->getTransaction()->getId()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn(new PartitionResponse([ + 'partitions' => [ + new Partition(['partition_token' => $partitionToken1]), + new Partition(['partition_token' => $partitionToken2]), ] - ] - ]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + ])); $res = $this->operation->partitionQuery($this->session, $transactionId, $sql, [ 'parameters' => $params @@ -512,39 +559,36 @@ public function testPartitionQuery() public function testPartitionRead() { - $sql = 'SELECT * FROM Posts WHERE ID = @id'; $params = ['id' => 10]; $transactionId = 'foo'; $partitionToken1 = 'token1'; $partitionToken2 = 'token2'; - $this->connection->partitionRead(Argument::allOf( - Argument::withEntry('table', 'Posts'), - Argument::withEntry('session', self::SESSION), - Argument::withEntry('keySet', ['all' => true]), - Argument::withEntry('columns', ['foo']) - ))->shouldBeCalled()->willReturn([ - 'partitions' => [ - [ - 'partitionToken' => $partitionToken1 - ], [ - 'partitionToken' => $partitionToken2 + $this->spannerClient->partitionRead( + Argument::that(function ($request) { + $this->assertEquals('Posts', $request->getTable()); + $this->assertEquals(self::SESSION, $request->getSession()); + $this->assertEquals(true, $request->getKeySet()->getAll()); + $this->assertEquals(['foo'], $this->serializer->encodeMessage($request)['columns']); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn(new PartitionResponse([ + 'partitions' => [ + new Partition(['partition_token' => $partitionToken1]), + new Partition(['partition_token' => $partitionToken2]), ] - ] - ]); - - $this->operation->___setProperty('connection', $this->connection->reveal()); + ])); $res = $this->operation->partitionRead( $this->session, $transactionId, 'Posts', new KeySet(['all' => true]), - ['foo'], - [ - 'parameters' => $params - ] + ['foo'] ); $this->assertContainsOnlyInstancesOf(ReadPartition::class, $res); @@ -553,24 +597,44 @@ public function testPartitionRead() $this->assertEquals($partitionToken2, $res[1]->token()); } - private function executeAndReadResponse(array $additionalMetadata = []) + private function executeAndReadResponseStream(string $transactionId = null) + { + $stream = $this->prophesize(ServerStream::class); + $stream->readAll()->willReturn($this->executeAndReadResponse($transactionId)); + + return $stream->reveal(); + } + + private function executeAndReadResponse(string $transactionId = null) { - yield [ - 'metadata' => array_merge([ - 'rowType' => [ + $transactionMetadata = []; + if ($transactionId) { + $transactionMetadata = ['transaction' => new TransactionProto(['id' => $transactionId])]; + } + yield new PartialResultSet([ + 'metadata' => new ResultSetMetadata([ + 'row_type' => new StructType([ 'fields' => [ - [ + new Field([ 'name' => 'ID', - 'type' => [ - 'code' => Database::TYPE_INT64 - ] - ] + 'type' => new Type(['code' => Database::TYPE_INT64]) + ]), ] - ] - ], $additionalMetadata), + ]) + ] + $transactionMetadata), 'values' => [ - '10' + new Value(['string_value' => '10']) ] - ]; + ]); + } + + private function commitResponse($commit = []) + { + return new CommitResponse($commit + [ + 'commit_timestamp' => new TimestampProto([ + 'seconds' => (new \DateTime(self::TIMESTAMP))->format('U'), + 'nanos' => 534799000 + ]) + ]); } } diff --git a/Spanner/tests/Unit/ResultTest.php b/Spanner/tests/Unit/ResultTest.php index 05d8f17ac8e5..89aa0785811a 100644 --- a/Spanner/tests/Unit/ResultTest.php +++ b/Spanner/tests/Unit/ResultTest.php @@ -22,10 +22,13 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Snapshot; use Google\Cloud\Spanner\ValueMapper; +use Google\Cloud\Spanner\Operation; +use Google\Cloud\Spanner\Result; use Google\Cloud\Core\Testing\GrpcTestTrait; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; /** * @group spanner @@ -35,9 +38,9 @@ class ResultTest extends TestCase { use GrpcTestTrait; use ProphecyTrait; - use ResultTestTrait; + use ResultGeneratorTrait; - private $metadata = [ + private const METADATA = [ 'rowType' => [ 'fields' => [ [ @@ -47,10 +50,38 @@ class ResultTest extends TestCase ] ] ]; + private $operation; + private $session; + private $transaction; + private $snapshot; public function setUp(): void { $this->checkAndSkipGrpcTests(); + + $this->operation = $this->prophesize(Operation::class); + $this->session = $this->prophesize(Session::class); + $this->transaction = $this->prophesize(Transaction::class); + $this->snapshot = $this->prophesize(Snapshot::class); + + $this->mapper = $this->prophesize(ValueMapper::class); + $this->mapper->decodeValues( + Argument::any(), + Argument::any(), + Argument::any() + )->will(function ($args) { + return $args[1]; + }); + + $this->operation->createSnapshot( + $this->session->reveal(), + Argument::type('array') + )->willReturn($this->snapshot->reveal()); + + $this->operation->createTransaction( + $this->session->reveal(), + Argument::type('array') + )->willReturn($this->transaction->reveal()); } /** @@ -58,31 +89,47 @@ public function setUp(): void */ public function testRows($chunks, $expectedValues) { - $result = iterator_to_array($this->getResultClass($chunks)->rows()); - $this->assertEquals($expectedValues, $result); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($chunks) { + return $this->resultGeneratorJson($chunks); + }, + 'r', + $this->mapper->reveal() + ); + $this->assertEquals($expectedValues, iterator_to_array($result->rows())); } public function testIterator() { - $fixture = $this->getStreamingDataFixture()['tests'][0]; - $result = iterator_to_array($this->getResultClass($fixture['chunks'])); + $fixtures = $this->getStreamingDataFixture()['tests'][0]; + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixtures) { + return $this->resultGeneratorJson($fixtures['chunks']); + }, + 'r', + $this->mapper->reveal() + ); - $this->assertEquals($fixture['result']['value'], $result); + $this->assertEquals($fixtures['result']['value'], iterator_to_array($result)); } public function testFailsWhenStreamThrowsUnrecoverableException() { $this->expectException(\Exception::class); - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () { - throw new \Exception; - } + throw new \Exception(); + }, + 'r', + $this->mapper->reveal() ); - iterator_to_array($result->rows()); } @@ -91,22 +138,19 @@ public function testResumesBrokenStream() $timesCalled = 0; $chunks = [ [ - 'metadata' => $this->metadata, + 'metadata' => self::METADATA, 'values' => ['a'] ], [ 'values' => ['b'], 'resumeToken' => 'abc' ], - [ - 'values' => ['c'] - ] + ['values' => ['c']] ]; - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () use ($chunks, &$timesCalled) { $timesCalled++; @@ -116,7 +160,9 @@ function () use ($chunks, &$timesCalled) { } yield $chunk; } - } + }, + 'r', + $this->mapper->reveal() ); iterator_to_array($result->rows()); @@ -128,21 +174,16 @@ public function testResumesAfterStreamStartFailure() $timesCalled = 0; $chunks = [ [ - 'metadata' => $this->metadata, + 'metadata' => self::METADATA, 'values' => ['a'] ], - [ - 'values' => ['b'] - ], - [ - 'values' => ['c'] - ] + ['values' => ['b']], + ['values' => ['c']] ]; - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () use ($chunks, &$timesCalled) { $timesCalled++; if ($timesCalled === 1) { @@ -152,7 +193,9 @@ function () use ($chunks, &$timesCalled) { foreach ($chunks as $key => $chunk) { yield $chunk; } - } + }, + 'r', + $this->mapper->reveal() ); iterator_to_array($result->rows()); @@ -164,21 +207,16 @@ public function testRowsRetriesWithoutResumeTokenWhenNotYieldedRows() $timesCalled = 0; $chunks = [ [ - 'metadata' => $this->metadata, + 'metadata' => self::METADATA, 'values' => ['a'] ], - [ - 'values' => ['b'] - ], - [ - 'values' => ['c'] - ] + ['values' => ['b']], + ['values' => ['c']] ]; - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () use ($chunks, &$timesCalled) { $timesCalled++; foreach ($chunks as $key => $chunk) { @@ -187,7 +225,9 @@ function () use ($chunks, &$timesCalled) { } yield $chunk; } - } + }, + 'r', + $this->mapper->reveal() ); iterator_to_array($result->rows()); @@ -199,22 +239,17 @@ public function testRowsRetriesWithResumeTokenWhenNotYieldedRows() $timesCalled = 0; $chunks = [ [ - 'metadata' => $this->metadata, + 'metadata' => self::METADATA, 'values' => ['a'], 'resumeToken' => 'abc' ], - [ - 'values' => ['b'] - ], - [ - 'values' => ['c'] - ] + ['values' => ['b']], + ['values' => ['c']] ]; - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () use ($chunks, &$timesCalled) { $timesCalled++; foreach ($chunks as $key => $chunk) { @@ -223,7 +258,9 @@ function () use ($chunks, &$timesCalled) { } yield $chunk; } - } + }, + 'r', + $this->mapper->reveal() ); iterator_to_array($result->rows()); @@ -236,18 +273,15 @@ public function testThrowsExceptionWhenCannotRetry() $chunks = [ [ - 'metadata' => $this->metadata, + 'metadata' => self::METADATA, 'values' => ['a'] ], - [ - 'values' => ['b'] - ] + ['values' => ['b']] ]; - $result = $this->getResultClass( - null, - 'r', - null, + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), function () use ($chunks) { foreach ($chunks as $key => $chunk) { if ($key === 1) { @@ -255,7 +289,9 @@ function () use ($chunks) { } yield $chunk; } - } + }, + 'r', + $this->mapper->reveal() ); iterator_to_array($result->rows()); @@ -264,7 +300,15 @@ function () use ($chunks) { public function testColumns() { $fixture = $this->getStreamingDataFixture()['tests'][0]; - $result = $this->getResultClass($fixture['chunks']); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $this->mapper->reveal() + ); $expectedColumnNames = ['f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7']; $this->assertNull($result->columns()); @@ -275,7 +319,15 @@ public function testColumns() public function testMetadata() { $fixture = $this->getStreamingDataFixture()['tests'][0]; - $result = $this->getResultClass($fixture['chunks']); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $this->mapper->reveal() + ); $expectedMetadata = json_decode($fixture['chunks'][0], true)['metadata']; $this->assertNull($result->stats()); @@ -286,7 +338,15 @@ public function testMetadata() public function testSession() { $fixture = $this->getStreamingDataFixture()['tests'][0]; - $result = $this->getResultClass($fixture['chunks']); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $this->mapper->reveal() + ); $this->assertInstanceOf(Session::class, $result->session()); } @@ -294,7 +354,15 @@ public function testSession() public function testStats() { $fixture = $this->getStreamingDataFixture()['tests'][1]; - $result = $this->getResultClass($fixture['chunks']); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $this->mapper->reveal() + ); $expectedStats = json_decode($fixture['chunks'][0], true)['stats']; $this->assertNull($result->stats()); @@ -305,7 +373,15 @@ public function testStats() public function testTransaction() { $fixture = $this->getStreamingDataFixture()['tests'][1]; - $result = $this->getResultClass($fixture['chunks'], 'rw'); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'rw', + $this->mapper->reveal() + ); $this->assertInstanceOf(Transaction::class, $result->transaction()); } @@ -313,7 +389,15 @@ public function testTransaction() public function testSnapshot() { $fixture = $this->getStreamingDataFixture()['tests'][1]; - $result = $this->getResultClass($fixture['chunks']); + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $this->mapper->reveal() + ); $this->assertInstanceOf(Snapshot::class, $result->snapshot()); } @@ -327,7 +411,16 @@ public function testUsesCorrectDefaultFormatOption() Argument::any(), 'associative' )->shouldBeCalled(); - $result = $this->getResultClass($fixture['chunks'], 'r', $mapper->reveal()); + + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $mapper->reveal() + ); $rows = $result->rows(); $rows->current(); @@ -345,7 +438,16 @@ public function testRecievesCorrectFormatOption($format) Argument::any(), $format )->shouldBeCalled(); - $result = $this->getResultClass($fixture['chunks'], 'r', $mapper->reveal()); + + $result = new Result( + $this->operation->reveal(), + $this->session->reveal(), + function () use ($fixture) { + return $this->resultGeneratorJson($fixture['chunks']); + }, + 'r', + $mapper->reveal() + ); $rows = $result->rows($format); $rows->current(); diff --git a/Spanner/tests/Unit/ResultTestTrait.php b/Spanner/tests/Unit/ResultTestTrait.php deleted file mode 100644 index 713fa4a14d01..000000000000 --- a/Spanner/tests/Unit/ResultTestTrait.php +++ /dev/null @@ -1,112 +0,0 @@ -getStreamingDataFixture()['tests'] as $test) { - yield [$test['chunks'], $test['result']['value']]; - } - } - - public function streamingDataProviderFirstChunk() - { - foreach ($this->getStreamingDataFixture()['tests'] as $test) { - yield [$test['chunks'], $test['result']['value']]; - break; - } - } - - private function getResultClass( - $chunks = null, - $context = 'r', - $mapper = null, - $call = null - ) { - $operation = $this->prophesize(Operation::class); - $session = $this->prophesize(Session::class)->reveal(); - $transaction = $this->prophesize(Transaction::class); - $snapshot = $this->prophesize(Snapshot::class); - - if (!$mapper) { - $mapper = $this->prophesize(ValueMapper::class); - $mapper->decodeValues( - Argument::any(), - Argument::any(), - Argument::any() - )->will(function ($args) { - return $args[1]; - }); - $mapper = $mapper->reveal(); - } - - if (!$call) { - $call = function () use ($chunks) { - return $this->resultGenerator($chunks); - }; - } - - if ($context === 'r') { - $operation->createSnapshot( - $session, - Argument::type('array') - )->willReturn($snapshot->reveal()); - } else { - $operation->createTransaction( - $session, - Argument::type('array') - )->willReturn($transaction->reveal()); - } - - return new Result( - $operation->reveal(), - $session, - $call, - $context, - $mapper - ); - } - - private function resultGenerator($chunks) - { - foreach ($chunks as $chunk) { - yield json_decode($chunk, true); - } - } - - private function getStreamingDataFixture() - { - return json_decode( - file_get_contents(Fixtures::STREAMING_READ_ACCEPTANCE_FIXTURE()), - true - ); - } -} diff --git a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php index 7a4fd47ee651..7dab5dcb57f9 100644 --- a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php +++ b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php @@ -19,11 +19,11 @@ use Google\Auth\Cache\MemoryCacheItemPool; use Google\Cloud\Core\Testing\Lock\MockValues; -use Google\Cloud\Spanner\Connection\Grpc; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Session\CacheSessionPool; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Core\RequestHandler; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\RejectedPromise; use Psr\Cache\CacheItemInterface; @@ -174,7 +174,7 @@ public function testAcquireThrowsExceptionWithNoAvailableSessions() public function testAcquireRemovesToCreateItemsIfCreateCallFails() { $exceptionThrown = false; - $config = ['maxSessions' => 1]; + $config = ['maxSessions' => 1, 'sleepIntervalSeconds' => 0]; $pool = new CacheSessionPoolStub($this->getCacheItemPool(), $config, $this->time); $pool->setDatabase($this->getDatabase(true)); @@ -195,9 +195,10 @@ public function testAcquireRemovesToCreateItemsIfCreateCallFails() public function testAcquireIfCreateSessionCallFails() { + $config = ['sleepIntervalSeconds' => 0]; $exceptionThrown = false; $exceptionMessage = null; - $pool = new CacheSessionPoolStub($this->getCacheItemPool()); + $pool = new CacheSessionPoolStub($this->getCacheItemPool(), $config); $pool->setDatabase($this->getDatabase(true)); try { @@ -857,7 +858,7 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f { $database = $this->prophesize(DatabaseStub::class); $session = $this->prophesize(SessionStub::class); - $connection = $this->prophesize(Grpc::class); + $requestHandler = $this->prophesize(RequestHandler::class); $session->expiration() ->willReturn($this->time + 3600); @@ -866,18 +867,17 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f if ($willDeleteSessions) { $session->delete() ->willReturn(null); - $connection->deleteSessionAsync(Argument::any()) + $database->deleteSessionAsync(Argument::any()) ->willReturn(new FulfilledPromise( new DumbObject() )); } else { - $connection->deleteSessionAsync(Argument::any()) + $database->deleteSessionAsync(Argument::any()) ->willReturn(new RejectedPromise( new DumbObject() )); } - $database->connection() - ->willReturn($connection->reveal()); + $database->session(Argument::any()) ->will(function ($args) use ($session) { $session->name() @@ -916,11 +916,11 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f }; if ($expectedCreateCalls) { - $connection->batchCreateSessions(Argument::any()) + $database->batchCreateSessions(Argument::any()) ->shouldBeCalledTimes($expectedCreateCalls) ->will($createRes); } else { - $connection->batchCreateSessions(Argument::any()) + $database->batchCreateSessions(Argument::any()) ->will($createRes); } @@ -1188,13 +1188,10 @@ public function testSessionPoolDatabaseRole() ]); $database->name() ->willReturn(self::DATABASE_NAME); - $connection = $this->prophesize(Grpc::class); - $connection->batchCreateSessions(['database' => self::DATABASE_NAME, + $database->batchCreateSessions([ 'sessionTemplate' => ['labels' => [], 'creator_role' => 'Reader'], 'sessionCount' => 1]) ->shouldBeCalled() ->willReturn(['session' => array(['name' => 'session', 'expirtation' => $this->time])]); - $database->connection() - ->willReturn($connection->reveal()); $pool->setDatabase($database->reveal()); $pool->warmup(); diff --git a/Spanner/tests/Unit/SpannerClientTest.php b/Spanner/tests/Unit/SpannerClientTest.php index beeb17033339..fce9eb805e42 100644 --- a/Spanner/tests/Unit/SpannerClientTest.php +++ b/Spanner/tests/Unit/SpannerClientTest.php @@ -17,19 +17,26 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\ApiCore\OperationResponse; +use Google\ApiCore\Serializer; +use Google\ApiCore\PagedListResponse; +use Google\ApiCore\Page; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\LongRunning\LongRunningOperation; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Core\Testing\Snippet\Fixtures; use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsResponse; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesResponse; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; +use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Date; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\InstanceConfiguration; use Google\Cloud\Spanner\PgJsonb; @@ -39,8 +46,9 @@ use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\SpannerClient; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; +use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\Protobuf\Duration; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Prophecy\Argument; @@ -53,22 +61,25 @@ class SpannerClientTest extends TestCase { use GrpcTestTrait; use ProphecyTrait; - use StubCreationTrait; const PROJECT = 'my-awesome-project'; const INSTANCE = 'inst'; const DATABASE = 'db'; const CONFIG = 'conf'; - private $client; - private $connection; + private $serializer; + private SpannerClient $spannerClient; + private $instanceAdminClient; private $directedReadOptionsIncludeReplicas; + private $operationResponse; + public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); + $this->serializer = new Serializer(); + $this->directedReadOptionsIncludeReplicas = [ 'includeReplicas' => [ 'replicaSelections' => [ @@ -78,17 +89,23 @@ public function setUp(): void ] ] ]; - $this->client = TestHelpers::stub(SpannerClient::class, [ - [ - 'projectId' => self::PROJECT, - 'directedReadOptions' => $this->directedReadOptionsIncludeReplicas - ] + + $this->instanceAdminClient = $this->prophesize(InstanceAdminClient::class); + $this->spannerClient = new SpannerClient([ + 'projectId' => self::PROJECT, + 'credentials' => Fixtures::KEYFILE_STUB_FIXTURE(), + 'directedReadOptions' => $this->directedReadOptionsIncludeReplicas, + 'gapicSpannerInstanceAdminClient' => $this->instanceAdminClient->reveal() ]); + + $this->operationResponse = $this->prophesize(OperationResponse::class); + $this->operationResponse->withResultFunction(Argument::type('callable')) + ->willReturn($this->operationResponse->reveal()); } public function testBatch() { - $batch = $this->client->batch('foo', 'bar'); + $batch = $this->spannerClient->batch('foo', 'bar'); $this->assertInstanceOf(BatchClient::class, $batch); $ref = new \ReflectionObject($batch); @@ -96,8 +113,7 @@ public function testBatch() $prop->setAccessible(true); $this->assertEquals( - sprintf( - 'projects/%s/instances/%s/databases/%s', + GapicSpannerClient::databaseName( self::PROJECT, 'foo', 'bar' @@ -111,25 +127,34 @@ public function testBatch() */ public function testInstanceConfigurations() { - $this->connection->listInstanceConfigs( - Argument::withEntry('projectName', InstanceAdminClient::projectName(self::PROJECT)) - ) - ->shouldBeCalled() - ->willReturn([ - 'instanceConfigs' => [ - [ + $page = $this->prophesize(Page::class); + $page->getResponseObject() + ->willReturn(new ListInstanceConfigsResponse([ + 'instance_configs' => [ + new InstanceConfig([ 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG), - 'displayName' => 'Bar' - ], [ + 'display_name' => 'Bar' + ]), + new InstanceConfig([ 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG), - 'displayName' => 'Bat' - ] + 'display_name' => 'Bat' + ]), ] - ]); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ])); + $pagedListResponse = $this->prophesize(PagedListResponse::class); + $pagedListResponse->getPage() + ->willReturn($page->reveal()); + + $this->instanceAdminClient->listInstanceConfigs( + Argument::that(function ($request) { + return $request->getParent() == InstanceAdminClient::projectName(self::PROJECT); + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn($pagedListResponse->reveal()); - $configs = $this->client->instanceConfigurations(); + $configs = $this->spannerClient->instanceConfigurations(); $this->assertInstanceOf(ItemIterator::class, $configs); @@ -144,34 +169,60 @@ public function testInstanceConfigurations() */ public function testPagedInstanceConfigurations() { - $firstCall = [ - 'instanceConfigs' => [ - [ - 'name' => 'projects/foo/instanceConfigs/bar', - 'displayName' => 'Bar' + $page1 = $this->prophesize(Page::class); + $page1->getResponseObject() + ->willReturn(new ListInstanceConfigsResponse([ + 'instance_configs' => [ + new InstanceConfig([ + 'name' => 'projects/foo/instanceConfigs/bar', + 'display_name' => 'Bar' + ]) + ], + 'next_page_token' => 'fooBar' + ])); + + $pagedListResponse1 = $this->prophesize(PagedListResponse::class); + $pagedListResponse1->getPage() + ->willReturn($page1->reveal()); + + $page2 = $this->prophesize(Page::class); + $page2->getResponseObject() + ->willReturn(new ListInstanceConfigsResponse([ + 'instance_configs' => [ + new InstanceConfig([ + 'name' => 'projects/foo/instanceConfigs/bat', + 'display_name' => 'Bat' + ]) ] - ], - 'nextPageToken' => 'fooBar' - ]; - - $secondCall = [ - 'instanceConfigs' => [ - [ - 'name' => 'projects/foo/instanceConfigs/bat', - 'displayName' => 'Bat' - ] - ] - ]; - - $this->connection->listInstanceConfigs( - Argument::withEntry('projectName', InstanceAdminClient::projectName(self::PROJECT)) + ])); + + $pagedListResponse2 = $this->prophesize(PagedListResponse::class); + $pagedListResponse2->getPage() + ->willReturn($page2->reveal()); + + $iteration = 0; + $this->instanceAdminClient->listInstanceConfigs( + Argument::that(function ($request) use (&$iteration) { + $iteration++; + return $this->serializer->encodeMessage($request)['parent'] + == InstanceAdminClient::projectName(self::PROJECT) && $iteration == 1; + }), + Argument::type('array') ) - ->shouldBeCalledTimes(2) - ->willReturn($firstCall, $secondCall); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ->shouldBeCalled() + ->willReturn($pagedListResponse1->reveal()); + + $this->instanceAdminClient->listInstanceConfigs( + Argument::that(function ($request) use (&$iteration) { + return $this->serializer->encodeMessage($request)['parent'] + == InstanceAdminClient::projectName(self::PROJECT) && $iteration == 2; + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn($pagedListResponse2->reveal()); - $configs = $this->client->instanceConfigurations(); + $configs = $this->spannerClient->instanceConfigurations(); $this->assertInstanceOf(ItemIterator::class, $configs); @@ -186,7 +237,7 @@ public function testPagedInstanceConfigurations() */ public function testInstanceConfiguration() { - $config = $this->client->instanceConfiguration('bar'); + $config = $this->spannerClient->instanceConfiguration('bar'); $this->assertInstanceOf(InstanceConfiguration::class, $config); $this->assertEquals('bar', InstanceAdminClient::parseName($config->name())['instance_config']); @@ -197,26 +248,30 @@ public function testInstanceConfiguration() */ public function testCreateInstance() { - $this->connection->createInstance(Argument::that(function ($arg) { - if ($arg['name'] !== InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)) { - return false; - } - - return $arg['config'] === InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG); - })) + $this->instanceAdminClient->createInstance( + Argument::that(function ($request) use (&$iteration) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals( + $message['instance']['name'], + InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE) + ); + $this->assertEquals( + $message['instance']['config'], + InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG) + ); + return true; + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn([ - 'name' => 'operations/foo' - ]); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ->willReturn($this->operationResponse->reveal()); $config = $this->prophesize(InstanceConfiguration::class); $config->name()->willReturn(InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG)); - $operation = $this->client->createInstance($config->reveal(), self::INSTANCE); + $operation = $this->spannerClient->createInstance($config->reveal(), self::INSTANCE); - $this->assertInstanceOf(LongRunningOperation::class, $operation); + $this->assertInstanceOf(OperationResponse::class, $operation); } /** @@ -224,32 +279,35 @@ public function testCreateInstance() */ public function testCreateInstanceWithNodes() { - $this->connection->createInstance(Argument::that(function ($arg) { - if ($arg['name'] !== InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)) { - return false; - } - - if ($arg['config'] !== InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG)) { - return false; - } - - return isset($arg['nodeCount']) && $arg['nodeCount'] === 2; - })) + $this->instanceAdminClient->createInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + if ($message['instance']['name'] !== InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)) { + return false; + } + + if ($message['instance']['config'] !== InstanceAdminClient::instanceConfigName( + self::PROJECT, + self::CONFIG + )) { + return false; + } + + return isset($message['instance']['nodeCount']) && $message['instance']['nodeCount'] === 2; + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn([ - 'name' => 'operations/foo' - ]); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ->willReturn($this->operationResponse->reveal()); $config = $this->prophesize(InstanceConfiguration::class); $config->name()->willReturn(InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG)); - $operation = $this->client->createInstance($config->reveal(), self::INSTANCE, [ + $operation = $this->spannerClient->createInstance($config->reveal(), self::INSTANCE, [ 'nodeCount' => 2 ]); - $this->assertInstanceOf(LongRunningOperation::class, $operation); + $this->assertInstanceOf(OperationResponse::class, $operation); } /** @@ -257,32 +315,38 @@ public function testCreateInstanceWithNodes() */ public function testCreateInstanceWithProcessingUnits() { - $this->connection->createInstance(Argument::that(function ($arg) { - if ($arg['name'] !== InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)) { - return false; - } - - if ($arg['config'] !== InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG)) { - return false; - } - - return isset($arg['processingUnits']) && $arg['processingUnits'] === 2000; - })) + $this->instanceAdminClient->createInstance( + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + if ($message['instance']['name'] !== InstanceAdminClient::instanceName( + self::PROJECT, + self::INSTANCE + )) { + return false; + } + if ($message['instance']['config'] !== InstanceAdminClient::instanceConfigName( + self::PROJECT, + self::CONFIG + )) { + return false; + } + + return isset($message['instance']['processingUnits']) + && $message['instance']['processingUnits'] === 2000; + }), + Argument::type('array') + ) ->shouldBeCalled() - ->willReturn([ - 'name' => 'operations/foo' - ]); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ->willReturn($this->operationResponse->reveal()); $config = $this->prophesize(InstanceConfiguration::class); $config->name()->willReturn(InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG)); - $operation = $this->client->createInstance($config->reveal(), self::INSTANCE, [ + $operation = $this->spannerClient->createInstance($config->reveal(), self::INSTANCE, [ 'processingUnits' => 2000 ]); - $this->assertInstanceOf(LongRunningOperation::class, $operation); + $this->assertInstanceOf(OperationResponse::class, $operation); } /** @@ -294,7 +358,7 @@ public function testCreateInstanceRaisesInvalidArgument() $config = $this->prophesize(InstanceConfiguration::class); - $this->client->createInstance($config->reveal(), self::INSTANCE, [ + $this->spannerClient->createInstance($config->reveal(), self::INSTANCE, [ 'nodeCount' => 2, 'processingUnits' => 2000, ]); @@ -305,7 +369,7 @@ public function testCreateInstanceRaisesInvalidArgument() */ public function testInstance() { - $i = $this->client->instance('foo'); + $i = $this->spannerClient->instance('foo'); $this->assertInstanceOf(Instance::class, $i); $this->assertEquals('foo', InstanceAdminClient::parseName($i->name())['instance']); } @@ -315,7 +379,7 @@ public function testInstance() */ public function testInstanceWithInstanceArray() { - $i = $this->client->instance('foo', ['key' => 'val']); + $i = $this->spannerClient->instance('foo', ['key' => 'val']); $this->assertEquals('val', $i->info()['key']); } @@ -324,20 +388,31 @@ public function testInstanceWithInstanceArray() */ public function testInstances() { - $this->connection->listInstances( - Argument::withEntry('projectName', InstanceAdminClient::projectName(self::PROJECT)) - ) - ->shouldBeCalled() - ->willReturn([ + $page = $this->prophesize(Page::class); + $page->getResponseObject() + ->willReturn(new ListInstancesResponse([ 'instances' => [ - ['name' => 'projects/test-project/instances/foo'], - ['name' => 'projects/test-project/instances/bar'], + new InstanceProto(['name' => 'projects/test-project/instances/foo']), + new InstanceProto(['name' => 'projects/test-project/instances/bar']), ] - ]); - - $this->client->___setProperty('connection', $this->connection->reveal()); + ])); + $pagedListResponse = $this->prophesize(PagedListResponse::class); + $pagedListResponse->getPage() + ->willReturn($page->reveal()); + $this->instanceAdminClient->listInstances( + Argument::that(function ($request) { + $this->assertEquals( + $request->getParent(), + InstanceAdminClient::projectName(self::PROJECT) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalled() + ->willReturn($pagedListResponse->reveal()); - $instances = $this->client->instances(); + $instances = $this->spannerClient->instances(); $this->assertInstanceOf(ItemIterator::class, $instances); $instances = iterator_to_array($instances); @@ -353,108 +428,108 @@ public function testResumeOperation() { $opName = 'operations/foo'; - $op = $this->client->resumeOperation($opName); - $this->assertInstanceOf(LongRunningOperation::class, $op); - $this->assertEquals($op->name(), $opName); + $op = $this->spannerClient->resumeOperation($opName); + $this->assertInstanceOf(OperationResponse::class, $op); + $this->assertEquals($op->getName(), $opName); } public function testConnect() { - $database = $this->client->connect(self::INSTANCE, self::DATABASE); + $database = $this->spannerClient->connect(self::INSTANCE, self::DATABASE); $this->assertInstanceOf(Database::class, $database); $this->assertEquals(self::DATABASE, DatabaseAdminClient::parseName($database->name())['database']); } public function testConnectWithInstance() { - $inst = $this->client->instance(self::INSTANCE); - $database = $this->client->connect($inst, self::DATABASE); + $inst = $this->spannerClient->instance(self::INSTANCE); + $database = $this->spannerClient->connect($inst, self::DATABASE); $this->assertInstanceOf(Database::class, $database); $this->assertEquals(self::DATABASE, DatabaseAdminClient::parseName($database->name())['database']); } public function testKeyset() { - $ks = $this->client->keySet(); + $ks = $this->spannerClient->keySet(); $this->assertInstanceOf(KeySet::class, $ks); } public function testKeyRange() { - $kr = $this->client->keyRange(); + $kr = $this->spannerClient->keyRange(); $this->assertInstanceOf(KeyRange::class, $kr); } public function testBytes() { - $b = $this->client->bytes('foo'); + $b = $this->spannerClient->bytes('foo'); $this->assertInstanceOf(Bytes::class, $b); $this->assertEquals(base64_encode('foo'), (string)$b); } public function testDate() { - $d = $this->client->date(new \DateTime); + $d = $this->spannerClient->date(new \DateTime); $this->assertInstanceOf(Date::class, $d); } public function testTimestamp() { - $ts = $this->client->timestamp(new \DateTime); + $ts = $this->spannerClient->timestamp(new \DateTime); $this->assertInstanceOf(Timestamp::class, $ts); } public function testNumeric() { - $n = $this->client->numeric('12345.123456789'); + $n = $this->spannerClient->numeric('12345.123456789'); $this->assertInstanceOf(Numeric::class, $n); } public function testPgNumeric() { - $decimalVal = $this->client->pgNumeric('12345.123456789'); + $decimalVal = $this->spannerClient->pgNumeric('12345.123456789'); $this->assertInstanceOf(PgNumeric::class, $decimalVal); - $scientificVal = $this->client->pgNumeric('1.09E100'); + $scientificVal = $this->spannerClient->pgNumeric('1.09E100'); $this->assertInstanceOf(PgNumeric::class, $scientificVal); } public function testPgJsonB() { - $strVal = $this->client->pgJsonb('{}'); + $strVal = $this->spannerClient->pgJsonb('{}'); $this->assertInstanceOf(PgJsonb::class, $strVal); - $arrVal = $this->client->pgJsonb(["a" => 1, "b" => 2]); + $arrVal = $this->spannerClient->pgJsonb(["a" => 1, "b" => 2]); $this->assertInstanceOf(PgJsonb::class, $arrVal); $stub = $this->prophesize('stdClass'); $stub->willImplement('JsonSerializable'); $stub->jsonSerialize()->willReturn(["a" => 1, "b" => null]); - $objVal = $this->client->pgJsonb($stub->reveal()); + $objVal = $this->spannerClient->pgJsonb($stub->reveal()); $this->assertInstanceOf(PgJsonb::class, $objVal); } public function testPgOid() { - $oidVal = $this->client->pgOid('123'); + $oidVal = $this->spannerClient->pgOid('123'); $this->assertInstanceOf(PgOid::class, $oidVal); } public function testInt64() { - $i64 = $this->client->int64('123'); + $i64 = $this->spannerClient->int64('123'); $this->assertInstanceOf(Int64::class, $i64); } public function testDuration() { - $d = $this->client->duration(10, 1); + $d = $this->spannerClient->duration(10, 1); $this->assertInstanceOf(Duration::class, $d); } public function testCommitTimestamp() { - $t = $this->client->commitTimestamp(); + $t = $this->spannerClient->commitTimestamp(); $this->assertInstanceOf(CommitTimestamp::class, $t); } @@ -462,12 +537,12 @@ public function testSpannerClientDatabaseRole() { $instance = $this->prophesize(Instance::class); $instance->database(Argument::any(), ['databaseRole' => 'Reader'])->shouldBeCalled(); - $this->client->connect($instance->reveal(), self::DATABASE, ['databaseRole' => 'Reader']); + $this->spannerClient->connect($instance->reveal(), self::DATABASE, ['databaseRole' => 'Reader']); } public function testSpannerClientWithDirectedRead() { - $instance = $this->client->instance('testInstance'); + $instance = $this->spannerClient->instance('testInstance'); $this->assertEquals( $instance->directedReadOptions(), $this->directedReadOptionsIncludeReplicas diff --git a/Spanner/tests/Unit/TransactionConfigurationTraitTest.php b/Spanner/tests/Unit/TransactionConfigurationTraitTest.php index 527d42706c98..cb3bdf40e651 100644 --- a/Spanner/tests/Unit/TransactionConfigurationTraitTest.php +++ b/Spanner/tests/Unit/TransactionConfigurationTraitTest.php @@ -19,10 +19,10 @@ use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\TransactionConfigurationTrait; +use Google\Protobuf\Duration; use PHPUnit\Framework\TestCase; /** @@ -47,8 +47,8 @@ public function setUp(): void $this->checkAndSkipGrpcTests(); $this->impl = new TransactionConfigurationTraitImplementation; - $this->duration = new Duration(10, 1); - $this->dur = ['seconds' => 10, 'nanos' => 1]; + $this->duration = new Duration(['seconds' => 10, 'nanos' => 1]); + $this->dur = new Duration(['seconds' => 10, 'nanos' => 1]); $this->directedReadOptionsIncludeReplicas = [ 'includeReplicas' => [ 'replicaSelections' => [ @@ -157,14 +157,6 @@ public function testTransactionSelectorInvalidContext() $this->impl->proxyTransactionSelector($args); } - public function testConfigureSnapshotOptionsInvalidExactStaleness() - { - $this->expectException(\BadMethodCallException::class); - - $args = ['exactStaleness' => 'foo']; - $this->impl->proxyConfigureSnapshotOptions($args); - } - public function testConfigureSnapshotOptionsInvalidMaxStaleness() { $this->expectException(\BadMethodCallException::class); diff --git a/Spanner/tests/Unit/TransactionTest.php b/Spanner/tests/Unit/TransactionTest.php index 1563a9c9c9b6..e58d69b6fe79 100644 --- a/Spanner/tests/Unit/TransactionTest.php +++ b/Spanner/tests/Unit/TransactionTest.php @@ -17,22 +17,33 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\BatchDmlResult; use Google\Cloud\Spanner\Database; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Spanner\Tests\OperationRefreshTrait; use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest; +use Google\Cloud\Spanner\V1\ExecuteBatchDmlResponse; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; +use Google\Cloud\Spanner\V1\ResultSet; +use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\RollbackRequest; +use Google\Cloud\Spanner\V1\ResultSetStats; +use Google\Cloud\Spanner\V1\CommitResponse; +use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; +use Google\Protobuf\Timestamp as TimestampProto; +use Google\Protobuf\Duration; +use Google\Rpc\Status; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Prophecy\Argument; @@ -44,11 +55,10 @@ class TransactionTest extends TestCase { use GrpcTestTrait; - use OperationRefreshTrait; use ProphecyTrait; use ResultGeneratorTrait; - use StubCreationTrait; use TimeTrait; + use ApiHelperTrait; const TIMESTAMP = '2017-01-09T18:05:22.534799Z'; @@ -60,49 +70,57 @@ class TransactionTest extends TestCase const TRANSACTION_TAG = 'my-transaction-tag'; const REQUEST_TAG = 'my-request-tag'; - private $connection; private $instance; private $session; private $database; private $operation; + private $serializer; + private $headers; + private $spannerClient; private $transaction; - private $singleUseTransaction; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->connection = $this->getConnStub(); - $this->operation = new Operation($this->connection->reveal(), false); + $this->serializer = new Serializer([], [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]); + $this->spannerClient = $this->prophesize(SpannerClient::class); + $this->operation = new Operation( + $this->spannerClient->reveal(), + $this->serializer, + false + ); $this->session = new Session( - $this->connection->reveal(), + $this->spannerClient->reveal(), + $this->serializer, self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION ); - $args = [ + $this->transaction = new Transaction( $this->operation, $this->session, self::TRANSACTION, false, self::TRANSACTION_TAG - ]; - - $props = [ - 'operation', 'readTimestamp', 'state' - ]; - - $this->transaction = TestHelpers::stub(Transaction::class, $args, $props); - - $args = [ - $this->operation, - $this->session, - ]; - $this->singleUseTransaction = TestHelpers::stub(Transaction::class, $args, $props); + ); } public function testSingleUseTagError() @@ -118,115 +136,25 @@ public function testSingleUseTagError() ); } - public function testInsert() - { - $this->transaction->insert('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['insert']['table']); - $this->assertEquals('foo', $mutations[0]['insert']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['insert']['values'][0]); - } - - public function testInsertBatch() - { - $this->transaction->insertBatch('Posts', [['foo' => 'bar']]); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['insert']['table']); - $this->assertEquals('foo', $mutations[0]['insert']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['insert']['values'][0]); - } - - public function testUpdate() - { - $this->transaction->update('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['update']['table']); - $this->assertEquals('foo', $mutations[0]['update']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['update']['values'][0]); - } - - public function testUpdateBatch() - { - $this->transaction->updateBatch('Posts', [['foo' => 'bar']]); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['update']['table']); - $this->assertEquals('foo', $mutations[0]['update']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['update']['values'][0]); - } - - public function testInsertOrUpdate() - { - $this->transaction->insertOrUpdate('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['insertOrUpdate']['table']); - $this->assertEquals('foo', $mutations[0]['insertOrUpdate']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['insertOrUpdate']['values'][0]); - } - - public function testInsertOrUpdateBatch() - { - $this->transaction->insertOrUpdateBatch('Posts', [['foo' => 'bar']]); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['insertOrUpdate']['table']); - $this->assertEquals('foo', $mutations[0]['insertOrUpdate']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['insertOrUpdate']['values'][0]); - } - - public function testReplace() - { - $this->transaction->replace('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['replace']['table']); - $this->assertEquals('foo', $mutations[0]['replace']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['replace']['values'][0]); - } - - public function testReplaceBatch() - { - $this->transaction->replaceBatch('Posts', [['foo' => 'bar']]); - - $mutations = $this->transaction->___getProperty('mutationData'); - - $this->assertEquals('Posts', $mutations[0]['replace']['table']); - $this->assertEquals('foo', $mutations[0]['replace']['columns'][0]); - $this->assertEquals('bar', $mutations[0]['replace']['values'][0]); - } - - public function testDelete() - { - $this->transaction->delete('Posts', new KeySet(['keys' => ['foo']])); - - $mutations = $this->transaction->___getProperty('mutationData'); - $this->assertEquals('Posts', $mutations[0]['delete']['table']); - $this->assertEquals('foo', $mutations[0]['delete']['keySet']['keys'][0]); - $this->assertArrayNotHasKey('all', $mutations[0]['delete']['keySet']); - } - public function testExecute() { $sql = 'SELECT * FROM Table'; - - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('sql', $sql), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getSql(), $sql); + return true; + }), + Argument::that(function (array $callOptions) { + $this->assertEquals( + $callOptions['headers']['x-goog-spanner-route-to-leader'], + ['true'] + ); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); $res = $this->transaction->execute($sql); $this->assertInstanceOf(Result::class, $res); @@ -237,19 +165,26 @@ public function testExecute() public function testExecuteUpdate() { $sql = 'UPDATE foo SET bar = @bar'; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('requestOptions', [ - 'requestTag' => self::REQUEST_TAG, - 'transactionTag' => self::TRANSACTION_TAG - ]), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true)); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); - $res = $this->transaction->executeUpdate($sql, ['requestOptions' => ['requestTag' => self::REQUEST_TAG]]); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $this->assertEquals($request->getSql(), $sql); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals( + $request->getRequestOptions()->getRequestTag(), + self::REQUEST_TAG + ); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream(null, new ResultSetStats(['row_count_exact' => 1]))); + $res = $this->transaction->executeUpdate($sql, ['requestOptions' => ['requestTag' => self::REQUEST_TAG]]); $this->assertEquals(1, $res); } @@ -269,109 +204,109 @@ public function testExecuteUpdateWithExcludeTxnFromChangeStreamsThrowsException( public function testDmlSeqno() { $sql = 'UPDATE foo SET bar = @bar'; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('seqno', 1), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true)); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); - $this->transaction->executeUpdate($sql, ['requestOptions' => ['requestTag' => self::REQUEST_TAG]]); - - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('seqno', 2), - Argument::withEntry('requestOptions', [ - 'requestTag' => self::REQUEST_TAG, - 'transactionTag' => self::TRANSACTION_TAG - ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator(true)); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); - $this->transaction->executeUpdate($sql, ['requestOptions' => ['requestTag' => self::REQUEST_TAG]]); - - $this->connection->executeBatchDml(Argument::allOf( - Argument::withEntry('seqno', 3), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]) - ))->shouldBeCalled()->willReturn([ - 'resultSets' => [] - ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) { + $this->assertEquals($request->getSeqno(), 1); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream(null, new ResultSetStats(['row_count_exact' => 1]))); + + $this->transaction->executeUpdate( + $sql, + ['requestOptions' => ['requestTag' => self::REQUEST_TAG]] + ); + + $this->spannerClient->executeBatchDml( + Argument::that(function (ExecuteBatchDmlRequest $request) { + $this->assertEquals( + $request->getSeqno(), + 2 + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteBatchDmlResponse(['result_sets' => []])); - $this->refreshOperation($this->transaction, $this->connection->reveal()); $this->transaction->executeUpdateBatch( - [ - ['sql' => 'SELECT 1'], - ], + [['sql' => 'SELECT 1']], ['requestOptions' => ['requestTag' => self::REQUEST_TAG]] ); } public function testExecuteUpdateBatch() { - $this->connection->executeBatchDml(Argument::allOf( - Argument::withEntry('statements', [ - [ - 'sql' => 'SELECT 1', - 'params' => [], - 'paramTypes' => [] - ], [ - 'sql' => 'SELECT @foo', - 'params' => [ - 'foo' => 'bar' - ], - 'paramTypes' => [ - 'foo' => [ - 'code' => Database::TYPE_STRING - ] - ] - ], [ - 'sql' => 'SELECT @foo', - 'params' => [ - 'foo' => null - ], - 'paramTypes' => [ - 'foo' => [ - 'code' => Database::TYPE_STRING - ] - ] + $this->spannerClient->executeBatchDml( + Argument::that(function (ExecuteBatchDmlRequest $request) { + $this->assertEquals( + $request->getRequestOptions()->getRequestTag(), + self::REQUEST_TAG + ); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + $statements = $request->getStatements(); + $this->assertEquals(3, count($statements)); + + $statement1 = $statements[0]; + $this->assertEquals('SELECT 1', $statement1->getSql()); + $this->assertEmpty($statement1->getParams()); + $this->assertEmpty($statement1->getParamTypes()); + + $statement2 = $statements[1]; + $this->assertEquals('SELECT @foo', $statement2->getSql()); + $this->assertEquals('bar', $statement2->getParams()->getFields()['foo']->getStringValue()); + $types = $statement2->getParamTypes(); + $this->assertEquals(Database::TYPE_STRING, $types['foo']->getCode()); + + $statement3 = $statements[2]; + $this->assertEquals('SELECT @foo', $statement3->getSql()); + $this->assertEmpty($statement3->getParams()->getFields()['foo']->getStringValue()); + $types = $statement3->getParamTypes(); + $this->assertEquals(Database::TYPE_STRING, $types['foo']->getCode()); + return true; + }), + Argument::that(function (array $callOptions) { + $this->assertEquals( + $callOptions['headers']['x-goog-spanner-route-to-leader'], + ['true'] + ); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteBatchDmlResponse([ + 'result_sets' => [ + new ResultSet([ + 'stats' => new ResultSetStats([ + 'row_count_exact' => 1 + ]) + ]), + new ResultSet([ + 'stats' => new ResultSetStats([ + 'row_count_exact' => 2 + ]) + ]), + new ResultSet([ + 'stats' => new ResultSetStats([ + 'row_count_exact' => 3 + ]) + ]) ] - ]), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn([ - 'resultSets' => [ - [ - 'stats' => [ - 'rowCountExact' => 1 - ] - ], [ - 'stats' => [ - 'rowCountExact' => 2 - ] - ], [ - 'stats' => [ - 'rowCountExact' => 3 - ] - ] - ] - ]); + ])); - $this->refreshOperation($this->transaction, $this->connection->reveal()); $res = $this->transaction->executeUpdateBatch( $this->bdmlStatements(), ['requestOptions' => ['requestTag' => self::REQUEST_TAG]] ); - $this->assertInstanceOf(BatchDmlResult::class, $res); $this->assertNull($res->error()); - $this->assertEquals([1,2,3], $res->rowCounts()); + $this->assertEquals([1, 2, 3], $res->rowCounts()); } public function testExecuteUpdateBatchError() @@ -382,28 +317,38 @@ public function testExecuteUpdateBatchError() 'details' => [] ]; - $this->connection->executeBatchDml(Argument::allOf( - Argument::withEntry('session', $this->session->name()), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]) - ))->shouldBeCalled()->willReturn([ - 'resultSets' => [ - [ - 'stats' => [ - 'rowCountExact' => 1 - ] - ], [ - 'stats' => [ - 'rowCountExact' => 2 - ] - ] - ], - 'status' => $err - ]); + $this->spannerClient->executeBatchDml( + Argument::that(function (ExecuteBatchDmlRequest $request) { + $this->assertEquals($request->getSession(), $this->session->name()); + $this->assertEquals( + $request->getRequestOptions()->getRequestTag(), + self::REQUEST_TAG + ); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteBatchDmlResponse([ + 'result_sets' => [ + new ResultSet([ + 'stats' => new ResultSetStats([ + 'row_count_exact' => 1 + ]) + ]), + new ResultSet([ + 'stats' => new ResultSetStats([ + 'row_count_exact' => 2 + ]) + ]) + ], + 'status' => new Status($err) + ])); - $this->refreshOperation($this->transaction, $this->connection->reveal()); $statements = $this->bdmlStatements(); $res = $this->transaction->executeUpdateBatch( $statements, @@ -451,16 +396,25 @@ public function testExecuteUpdateNonDml() $this->expectException(InvalidArgumentException::class); $sql = 'UPDATE foo SET bar = @bar'; - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('sql', $sql), - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) use ($sql) { + $this->assertEquals($request->getSql(), $sql); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals( + $request->getRequestOptions()->getRequestTag(), + self::REQUEST_TAG + ); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); + $res = $this->transaction->executeUpdate($sql, ['requestOptions' => ['requestTag' => self::REQUEST_TAG]]); $this->assertEquals(1, $res); @@ -471,19 +425,35 @@ public function testRead() $table = 'Table'; $opts = ['foo' => 'bar']; - $this->connection->streamingRead(Argument::allOf( - Argument::withEntry('transaction', ['id' => self::TRANSACTION]), - Argument::withEntry('table', $table), - Argument::withEntry('keySet', ['all' => true]), - Argument::withEntry('columns', ['ID']), - Argument::withEntry('requestOptions', [ - 'transactionTag' => self::TRANSACTION_TAG, - 'requestTag' => self::REQUEST_TAG - ]), - Argument::withEntry('headers', ['x-goog-spanner-route-to-leader' => ['true']]) - ))->shouldBeCalled()->willReturn($this->resultGenerator()); + $this->spannerClient->streamingRead( + Argument::that(function (ReadRequest $request) use ($table) { + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getTable(), $table); + $this->assertTrue($request->getKeySet()->getAll()); + $this->assertEquals(iterator_to_array($request->getColumns()), ['ID']); + $this->assertEquals( + $request->getRequestOptions()->getTransactionTag(), + self::TRANSACTION_TAG + ); + $this->assertEquals( + $request->getRequestOptions()->getRequestTag(), + self::REQUEST_TAG + ); + + return true; + }), + Argument::that(function (array $callOptions) { + $this->assertEquals( + $callOptions['headers']['x-goog-spanner-route-to-leader'], + ['true'] + ); + + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream()); - $this->refreshOperation($this->transaction, $this->connection->reveal()); $res = $this->transaction->read( $table, @@ -499,37 +469,50 @@ public function testRead() public function testCommit() { - $this->transaction->insert('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - $operation = $this->prophesize(Operation::class); $operation->commitWithResponse( $this->session, - $mutations, + Argument::that(function ($mutations) { + $this->assertEquals(1, count($mutations)); + $this->assertEquals('Posts', $mutations[0]['insert']['table']); + $this->assertEquals('foo', $mutations[0]['insert']['columns'][0]); + $this->assertEquals('bar', $mutations[0]['insert']['values'][0]); + return true; + }), [ 'transactionId' => self::TRANSACTION, 'requestOptions' => [ 'transactionTag' => self::TRANSACTION_TAG ] ] - )->shouldBeCalled()->willReturn($this->commitResponseWithCommitStats()); + ) + ->shouldBeCalled() + ->willReturn($this->commitResponseWithCommitStats()); - $this->transaction->___setProperty('operation', $operation->reveal()); + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); - $this->transaction->commit(['requestOptions' => ['requestTag' => 'unused']]); + $transaction->insert('Posts', ['foo' => 'bar']); + $transaction->commit(['requestOptions' => ['requestTag' => 'unused']]); } public function testCommitWithReturnCommitStats() { - $this->transaction->insert('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); - $operation = $this->prophesize(Operation::class); $operation->commitWithResponse( $this->session, - $mutations, + Argument::that(function ($mutations) { + $this->assertEquals(1, count($mutations)); + $this->assertEquals('Posts', $mutations[0]['insert']['table']); + $this->assertEquals('foo', $mutations[0]['insert']['columns'][0]); + $this->assertEquals('bar', $mutations[0]['insert']['values'][0]); + return true; + }), [ 'transactionId' => self::TRANSACTION, 'returnCommitStats' => true, @@ -537,26 +520,39 @@ public function testCommitWithReturnCommitStats() 'transactionTag' => self::TRANSACTION_TAG ] ] - )->shouldBeCalled()->willReturn($this->commitResponseWithCommitStats()); + ) + ->shouldBeCalled() + ->willReturn($this->commitResponseWithCommitStats()); - $this->transaction->___setProperty('operation', $operation->reveal()); + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); - $this->transaction->commit(['returnCommitStats' => true]); + $transaction->insert('Posts', ['foo' => 'bar']); + $transaction->commit(['returnCommitStats' => true]); - $this->assertEquals(['mutationCount' => 1], $this->transaction->getCommitStats()); + $this->assertEquals(['mutationCount' => 1], $transaction->getCommitStats()); } public function testCommitWithMaxCommitDelay() { - $duration = new Duration(0, 100000000); - $this->transaction->insert('Posts', ['foo' => 'bar']); - - $mutations = $this->transaction->___getProperty('mutationData'); + $duration = new Duration(['seconds' => 0, 'nanos' => 100000000]); $operation = $this->prophesize(Operation::class); $operation->commitWithResponse( $this->session, - $mutations, + Argument::that(function ($mutations) { + $this->assertEquals(1, count($mutations)); + $this->assertEquals('Posts', $mutations[0]['insert']['table']); + $this->assertEquals('foo', $mutations[0]['insert']['columns'][0]); + $this->assertEquals('bar', $mutations[0]['insert']['values'][0]); + + return true; + }), [ 'transactionId' => self::TRANSACTION, 'returnCommitStats' => true, @@ -565,32 +561,60 @@ public function testCommitWithMaxCommitDelay() 'transactionTag' => self::TRANSACTION_TAG ] ] - )->shouldBeCalled()->willReturn($this->commitResponseWithCommitStats()); - - $this->transaction->___setProperty('operation', $operation->reveal()); + ) + ->shouldBeCalled() + ->willReturn($this->commitResponseWithCommitStats()); - $this->transaction->commit([ + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); + $transaction->insert('Posts', ['foo' => 'bar']); + $transaction->commit([ 'returnCommitStats' => true, 'maxCommitDelay' => $duration ]); - $this->assertEquals(['mutationCount' => 1], $this->transaction->getCommitStats()); + $this->assertEquals(['mutationCount' => 1], $transaction->getCommitStats()); } public function testCommitInvalidState() { $this->expectException(\BadMethodCallException::class); - $this->transaction->___setProperty('state', 'foo'); - $this->transaction->commit(); + $operation = $this->prophesize(Operation::class); + $operation->commitWithResponse(Argument::cetera()) + ->shouldBeCalledOnce() + ->willReturn([[]]); + + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); + + // call "commit" to mock closing the state + $transaction->commit(); + + // transaction is considered closed after the first commit, so this should throw an exception + $transaction->commit(); } public function testRollback() { - $this->connection->rollback(Argument::withEntry('session', $this->session->name())) - ->shouldBeCalled(); - - $this->refreshOperation($this->transaction, $this->connection->reveal()); + $this->spannerClient->rollback( + Argument::that(function (RollbackRequest $request) { + $this->assertEquals($request->getSession(), $this->session->name()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); $this->transaction->rollback(); } @@ -599,8 +623,24 @@ public function testRollbackInvalidState() { $this->expectException(\BadMethodCallException::class); - $this->transaction->___setProperty('state', 'foo'); - $this->transaction->rollback(); + $operation = $this->prophesize(Operation::class); + $operation->commitWithResponse(Argument::cetera()) + ->shouldBeCalledOnce() + ->willReturn([[]]); + + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); + + // call "commit" to mock closing the state + $transaction->commit(); + + // transaction is considered closed after the first commit, so this should throw an exception + $transaction->rollback(); } public function testId() @@ -610,17 +650,37 @@ public function testId() public function testState() { - $this->assertEquals(Transaction::STATE_ACTIVE, $this->transaction->state()); + $operation = $this->prophesize(Operation::class); + $operation->commitWithResponse(Argument::cetera()) + ->shouldBeCalledOnce() + ->willReturn([[]]); + + $transaction = new Transaction( + $operation->reveal(), + $this->session, + self::TRANSACTION, + false, + self::TRANSACTION_TAG + ); + + $this->assertEquals(Transaction::STATE_ACTIVE, $transaction->state()); + + // call "commit" to mock closing the state + $transaction->commit(); - $this->transaction->___setProperty('state', Transaction::STATE_COMMITTED); - $this->assertEquals(Transaction::STATE_COMMITTED, $this->transaction->state()); + $this->assertEquals(Transaction::STATE_COMMITTED, $transaction->state()); } public function testInvalidReadContext() { $this->expectException(\BadMethodCallException::class); - $this->singleUseTransaction->execute('foo'); + + $singleUseTransaction = new Transaction( + $this->operation, + $this->session, + ); + $singleUseTransaction->execute('foo'); } public function testIsRetryFalse() @@ -630,14 +690,12 @@ public function testIsRetryFalse() public function testIsRetryTrue() { - $args = [ + $transaction = new Transaction( $this->operation, $this->session, self::TRANSACTION, true - ]; - - $transaction = TestHelpers::stub(Transaction::class, $args); + ); $this->assertTrue($transaction->isRetry()); } @@ -645,11 +703,6 @@ public function testIsRetryTrue() // ******* // Helpers - private function commitResponse() - { - return ['commitTimestamp' => self::TIMESTAMP]; - } - private function commitResponseWithCommitStats() { $time = $this->parseTimeString(self::TIMESTAMP); @@ -662,11 +715,4 @@ private function commitResponseWithCommitStats() ] ]; } - - private function assertTimestampIsCorrect($res) - { - $ts = new \DateTimeImmutable($this->commitResponse()['commitTimestamp']); - - $this->assertEquals($ts->format('Y-m-d\TH:i:s\Z'), $res->get()->format('Y-m-d\TH:i:s\Z')); - } } diff --git a/Spanner/tests/Unit/TransactionTypeTest.php b/Spanner/tests/Unit/TransactionTypeTest.php index d228794c863b..c3700ff46e80 100644 --- a/Spanner/tests/Unit/TransactionTypeTest.php +++ b/Spanner/tests/Unit/TransactionTypeTest.php @@ -17,26 +17,45 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; +use Google\ApiCore\Serializer; +use Google\ApiCore\ServerStream; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Database; -use Google\Cloud\Spanner\Duration; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; -use Google\Cloud\Spanner\Tests\StubCreationTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; -use Google\Cloud\Spanner\V1\SpannerClient; +use Google\Cloud\Spanner\V1\BeginTransactionRequest; +use Google\Cloud\Spanner\V1\BeginTransactionResponse; +use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\CommitRequest; +use Google\Cloud\Spanner\V1\CommitResponse; +use Google\Cloud\Spanner\V1\CreateSessionRequest; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; +use Google\Cloud\Spanner\V1\PartialResultSet; +use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\RollbackRequest; +use Google\Cloud\Spanner\V1\Session; +use Google\Cloud\Spanner\V1\Transaction as TransactionProto; +use Google\Cloud\Spanner\V1\TransactionOptions; +use Google\Cloud\Spanner\V1\TransactionSelector; +use Google\Cloud\Spanner\V1\TransactionOptions\PBReadOnly; +use Google\Cloud\Spanner\V1\TransactionOptions\ReadWrite; +use Google\Protobuf\Duration; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; +use Google\Protobuf\Timestamp as TimestampProto; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; /** * @group spanner @@ -44,10 +63,10 @@ */ class TransactionTypeTest extends TestCase { + use ApiHelperTrait; use GrpcTestTrait; use ProphecyTrait; - use ResultTestTrait; - use StubCreationTrait; + use ResultGeneratorTrait; use TimeTrait; const PROJECT = 'my-project'; @@ -56,43 +75,80 @@ class TransactionTypeTest extends TestCase const TRANSACTION = 'my-transaction'; const SESSION = 'my-session'; - private $connection; - + private $spannerClient; + private $serializer; private $timestamp; + private $protoTimestamp; public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->timestamp = (new Timestamp(\DateTime::createFromFormat('U', time()), 500000005))->formatAsString(); + $time = \DateTime::createFromFormat('U', time()); + $nanos = 500000005; + $this->timestamp = (new Timestamp($time, $nanos))->formatAsString(); + $this->protoTimestamp = new TimestampProto(['seconds' => $time->format('U'), 'nanos' => $nanos]); - $this->connection = $this->getConnStub(); + $this->spannerClient = $this->prophesize(SpannerClient::class); + $this->serializer = $this->prophesize(Serializer::class); - $this->connection->createSession( - Argument::withEntry('database', SpannerClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE)) + // mock serializer responses for sessions (used for streaming tests) + $this->serializer = $this->prophesize(Serializer::class); + $this->serializer->decodeMessage( + Argument::type(CreateSessionRequest::class), + Argument::type('array') + ) + ->willReturn(new CreateSessionRequest([ + 'database' => SpannerClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ])); + $this->serializer->encodeMessage(Argument::type(Session::class)) + ->willReturn(['name' => $this->getFullyQualifiedSessionName()]); + + $this->serializer->decodeMessage( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') ) - ->willReturn(['name' => SpannerClient::sessionName( - self::PROJECT, - self::INSTANCE, - self::DATABASE, - self::SESSION - )]); + ->willReturn(new DeleteSessionRequest()); + + $this->spannerClient->createSession( + Argument::that(function (CreateSessionRequest $request) { + $this->assertEquals( + $request->getDatabase(), + SpannerClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) + ); + return true; + }), + Argument::type('array') + ) + ->willReturn(new Session(['name' => $this->getFullyQualifiedSessionName()])); + + $this->spannerClient->deleteSession(Argument::cetera()) + ->shouldBeCalledOnce(); } public function testDatabaseRunTransactionPreAllocate() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readWrite' => [] - ]) - ))->shouldBeCalledTimes(1)->willReturn(['id' => self::TRANSACTION]); - - $this->connection->commit(Argument::withEntry('transactionId', self::TRANSACTION)) - ->shouldBeCalledTimes(1) - ->willReturn(['commitTimestamp' => $this->timestamp]); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals($request->getSession(), $this->getFullyQualifiedSessionName()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->protoTimestamp])); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $database->runTransaction(function ($t) { // Transaction gets created at the commit operation @@ -102,14 +158,22 @@ public function testDatabaseRunTransactionPreAllocate() public function testDatabaseRunTransactionSingleUse() { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->commit(Argument::withEntry('singleUseTransaction', ['readWrite' => []])) - ->shouldBeCalledTimes(1) - ->willReturn(['commitTimestamp' => $this->timestamp]); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals( + $request->getSingleUseTransaction(), + $this->createTransactionOptions() + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->protoTimestamp])); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $database->runTransaction(function ($t) { $this->assertNull($t->id()); @@ -120,15 +184,17 @@ public function testDatabaseRunTransactionSingleUse() public function testDatabaseTransactionPreAllocate() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readWrite' => [] - ]) - ))->shouldBeCalledTimes(1)->willReturn(['id' => self::TRANSACTION]); - - $database = $this->database($this->connection->reveal()); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals($request->getOptions(), $this->createTransactionOptions()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + $database = $this->database($this->spannerClient->reveal()); $transaction = $database->transaction(); $this->assertInstanceOf(Transaction::class, $transaction); @@ -137,10 +203,9 @@ public function testDatabaseTransactionPreAllocate() public function testDatabaseTransactionSingleUse() { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $transaction = $database->transaction(['singleUse' => true]); @@ -150,17 +215,22 @@ public function testDatabaseTransactionSingleUse() public function testDatabaseSnapshotPreAllocate() { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readOnly' => [ - 'strong' => true - ] - ]) - ))->shouldBeCalledTimes(1) - ->willReturn(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals( + $request->getOptions(), + $this->createTransactionOptions(['readOnly' => ['strong' => true]]) + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $database = $this->database($this->connection->reveal()); + $database = $database = $this->database( + $this->spannerClient->reveal(), + ); $snapshot = $database->snapshot(); @@ -170,10 +240,11 @@ public function testDatabaseSnapshotPreAllocate() public function testDatabaseSnapshotSingleUse() { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->connection->reveal()); + $database = $database = $this->database( + $this->spannerClient->reveal(), + ); $snapshot = $database->snapshot(['singleUse' => true]); @@ -191,25 +262,28 @@ public function testDatabaseSingleUseSnapshotMinReadTimestampAndMaxStaleness($ch $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); - $duration = new Duration($seconds, $nanos); + $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $transaction = [ 'singleUse' => [ 'readOnly' => [ - 'minReadTimestamp' => $this->timestamp, - 'maxStaleness' => [ - 'seconds' => $seconds, - 'nanos' => $nanos - ] + 'minReadTimestamp' => ['seconds' => $time[0]->format('U'), 'nanos' => $time[1]], + 'maxStaleness' => $duration, ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; - $database = $this->database($this->connection->reveal()); + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'singleUse' => true, 'minReadTimestamp' => $timestamp, @@ -226,13 +300,11 @@ public function testDatabasePreAllocatedSnapshotMinReadTimestamp() $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->executeStreamingSql(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->deleteSession(Argument::cetera())->shouldNotBeCalled(); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldNotbeCalled(); - - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'minReadTimestamp' => $timestamp, @@ -246,15 +318,13 @@ public function testDatabasePreAllocatedSnapshotMaxStaleness() $seconds = 1; $nanos = 2; - $duration = new Duration($seconds, $nanos); - - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); - $this->connection->executeStreamingSql(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->executeStreamingSql(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->deleteSession(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'maxStaleness' => $duration @@ -271,26 +341,27 @@ public function testDatabaseSnapshotSingleUseReadTimestampAndExactStaleness($chu $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); - $duration = new Duration($seconds, $nanos); - - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->executeStreamingSql(Argument::allOf( - Argument::withEntry('transaction', [ - 'singleUse' => [ - 'readOnly' => [ - 'readTimestamp' => $this->timestamp, - 'exactStaleness' => [ - 'seconds' => $seconds, - 'nanos' => $nanos - ] - ] + $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); + $transaction = [ + 'singleUse' => [ + 'readOnly' => [ + 'readTimestamp' => $this->formatTimestampForApi($this->timestamp), + 'exactStaleness' => $duration, ] - ]) - ))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ] + ]; + + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -311,28 +382,44 @@ public function testDatabaseSnapshotPreAllocateReadTimestampAndExactStaleness($c $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); - $duration = new Duration($seconds, $nanos); + $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); + $options = [ + 'readOnly' => [ + 'readTimestamp' => $this->formatTimestampForApi($this->timestamp), + 'exactStaleness' => $duration, + ] + ]; + $transaction = new TransactionProto(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($transaction); - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readOnly' => [ - 'readTimestamp' => $this->timestamp, - 'exactStaleness' => [ - 'seconds' => $seconds, - 'nanos' => $nanos - ] - ] - ]) - ))->shouldBeCalledTimes(1)->willReturn([ - 'id' => self::TRANSACTION - ]); + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + + $this->serializer->decodeMessage( + Argument::type(BeginTransactionRequest::class), + Argument::that(function (array $data) use ($options){ + $this->assertEquals($data['options'], $options); + return true; + }), + ) + ->shouldBeCalledOnce() + ->willReturn(new BeginTransactionRequest()); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', ['id' => self::TRANSACTION])) - ->shouldBeCalledTimes(1) - ->willReturn($this->resultGenerator($chunks)); + $this->serializer->encodeMessage($transaction) + ->shouldBeCalledOnce() + ->willReturn([]); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, ['singleUse' => $options]); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'readTimestamp' => $timestamp, @@ -347,18 +434,25 @@ public function testDatabaseSnapshotPreAllocateReadTimestampAndExactStaleness($c */ public function testDatabaseSingleUseSnapshotStrongConsistency($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $transaction = [ 'singleUse' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -373,22 +467,42 @@ public function testDatabaseSingleUseSnapshotStrongConsistency($chunks) */ public function testDatabasePreAllocatedSnapshotStrongConsistency($chunks) { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readOnly' => [ - 'strong' => true - ] - ]) - ))->shouldBeCalledTimes(1)->willReturn([ - 'id' => self::TRANSACTION - ]); + $options = [ + 'readOnly' => [ + 'strong' => true + ] + ]; + $transaction = new TransactionProto(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($transaction); + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + + $this->serializer->decodeMessage( + Argument::type(BeginTransactionRequest::class), + Argument::that(function (array $data) use ($options){ + $this->assertEquals($data['options'], $options); + return true; + }), + ) + ->shouldBeCalledOnce() + ->willReturn(new BeginTransactionRequest()); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', ['id' => self::TRANSACTION])) - ->shouldBeCalledTimes(1) - ->willReturn($this->resultGenerator($chunks)); + $this->serializer->encodeMessage($transaction) + ->shouldBeCalledOnce() + ->willReturn([]); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, ['singleUse' => $options]); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'strong' => true @@ -402,18 +516,23 @@ public function testDatabasePreAllocatedSnapshotStrongConsistency($chunks) */ public function testDatabaseSingleUseSnapshotDefaultsToStrongConsistency($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'singleUse' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -427,22 +546,42 @@ public function testDatabaseSingleUseSnapshotDefaultsToStrongConsistency($chunks */ public function testDatabasePreAllocatedSnapshotDefaultsToStrongConsistency($chunks) { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readOnly' => [ - 'strong' => true - ] - ]) - ))->shouldBeCalledTimes(1)->willReturn([ - 'id' => self::TRANSACTION - ]); + $options = [ + 'readOnly' => [ + 'strong' => true + ] + ]; + $transaction = new TransactionProto(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($transaction); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', ['id' => self::TRANSACTION])) - ->shouldBeCalledTimes(1) - ->willReturn($this->resultGenerator($chunks)); + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + + $this->serializer->decodeMessage( + Argument::type(BeginTransactionRequest::class), + Argument::that(function (array $data) use ($options){ + $this->assertEquals($data['options'], $options); + return true; + }), + ) + ->shouldBeCalledOnce() + ->willReturn(new BeginTransactionRequest()); - $database = $this->database($this->connection->reveal()); + $this->serializer->encodeMessage($transaction) + ->shouldBeCalledOnce() + ->willReturn([]); + + $serializer = $this->serializerForStreamingSql($chunks, ['singleUse' => $options]); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot(); @@ -454,22 +593,42 @@ public function testDatabasePreAllocatedSnapshotDefaultsToStrongConsistency($chu */ public function testDatabaseSnapshotReturnReadTimestamp($chunks) { - $this->connection->beginTransaction(Argument::allOf( - Argument::withEntry('singleUse', false), - Argument::withEntry('transactionOptions', [ - 'readOnly' => [ - 'returnReadTimestamp' => true - ] - ]) - ))->shouldBeCalledTimes(1)->willReturn([ - 'id' => self::TRANSACTION - ]); + $options = [ + 'readOnly' => [ + 'returnReadTimestamp' => true + ] + ]; + $transaction = new TransactionProto(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::type(BeginTransactionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($transaction); + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + + $this->serializer->decodeMessage( + Argument::type(BeginTransactionRequest::class), + Argument::that(function (array $data) use ($options){ + $this->assertEquals($data['options'], $options); + return true; + }), + ) + ->shouldBeCalledOnce() + ->willReturn(new BeginTransactionRequest()); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', ['id' => self::TRANSACTION])) - ->shouldBeCalledTimes(1) - ->willReturn($this->resultGenerator($chunks)); + $this->serializer->encodeMessage($transaction) + ->shouldBeCalledOnce() + ->willReturn([]); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, ['singleUse' => $options]); + $database = $this->database($this->spannerClient->reveal(), $serializer); $snapshot = $database->snapshot([ 'returnReadTimestamp' => true @@ -480,13 +639,20 @@ public function testDatabaseSnapshotReturnReadTimestamp($chunks) public function testDatabaseInsertSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals( + $request->getSingleUseTransaction(), + $this->createTransactionOptions() + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->protoTimestamp])); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $database->insert('Table', [ 'column' => 'value' @@ -495,13 +661,7 @@ public function testDatabaseInsertSingleUseReadWrite() public function testDatabaseInsertBatchSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->insertBatch('Table', [[ 'column' => 'value' @@ -510,13 +670,7 @@ public function testDatabaseInsertBatchSingleUseReadWrite() public function testDatabaseUpdateSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->update('Table', [ 'column' => 'value' @@ -525,13 +679,7 @@ public function testDatabaseUpdateSingleUseReadWrite() public function testDatabaseUpdateBatchSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->updateBatch('Table', [[ 'column' => 'value' @@ -540,13 +688,7 @@ public function testDatabaseUpdateBatchSingleUseReadWrite() public function testDatabaseInsertOrUpdateSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->insertOrUpdate('Table', [ 'column' => 'value' @@ -555,13 +697,7 @@ public function testDatabaseInsertOrUpdateSingleUseReadWrite() public function testDatabaseInsertOrUpdateBatchSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->insertOrUpdateBatch('Table', [[ 'column' => 'value' @@ -570,13 +706,7 @@ public function testDatabaseInsertOrUpdateBatchSingleUseReadWrite() public function testDatabaseReplaceSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->replace('Table', [ 'column' => 'value' @@ -585,13 +715,7 @@ public function testDatabaseReplaceSingleUseReadWrite() public function testDatabaseReplaceBatchSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->replaceBatch('Table', [[ 'column' => 'value' @@ -600,13 +724,7 @@ public function testDatabaseReplaceBatchSingleUseReadWrite() public function testDatabaseDeleteSingleUseReadWrite() { - $this->connection->commit(Argument::withEntry('singleUseTransaction', [ - 'readWrite' => [] - ]))->shouldBeCalled()->willReturn([ - 'commitTimestamp' => $this->timestamp - ]); - - $database = $this->database($this->connection->reveal()); + $database = $this->createMockedCommitDatabase(); $database->delete('Table', new KeySet); } @@ -616,18 +734,23 @@ public function testDatabaseDeleteSingleUseReadWrite() */ public function testDatabaseExecuteSingleUseReadOnly($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'singleUse' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->execute('SELECT * FROM Table')->rows()->current(); } @@ -636,18 +759,25 @@ public function testDatabaseExecuteSingleUseReadOnly($chunks) */ public function testDatabaseExecuteBeginReadOnly($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $transaction = [ 'begin' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->execute('SELECT * FROM Table', [ 'begin' => true ])->rows()->current(); @@ -658,16 +788,21 @@ public function testDatabaseExecuteBeginReadOnly($chunks) */ public function testDatabaseExecuteBeginReadWrite($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->executeStreamingSql(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'begin' => [ 'readWrite' => [] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingSql($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->execute('SELECT * FROM Table', [ 'begin' => true, 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE @@ -679,18 +814,23 @@ public function testDatabaseExecuteBeginReadWrite($chunks) */ public function testDatabaseReadSingleUseReadOnly($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->streamingRead(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'singleUse' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + $this->spannerClient->streamingRead( + Argument::type(ReadRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingRead($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->read('Table', new KeySet, [])->rows()->current(); } @@ -699,18 +839,24 @@ public function testDatabaseReadSingleUseReadOnly($chunks) */ public function testDatabaseReadBeginReadOnly($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->streamingRead(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'begin' => [ 'readOnly' => [ 'strong' => true ] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + + $this->spannerClient->streamingRead( + Argument::type(ReadRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingRead($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->read('Table', new KeySet, [], [ 'begin' => true ])->rows()->current(); @@ -721,16 +867,21 @@ public function testDatabaseReadBeginReadOnly($chunks) */ public function testDatabaseReadBeginReadWrite($chunks) { - $this->connection->beginTransaction(Argument::any()) - ->shouldNotbeCalled(); - - $this->connection->streamingRead(Argument::withEntry('transaction', [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = [ 'begin' => [ 'readWrite' => [] ] - ]))->shouldBeCalledTimes(1)->willReturn($this->resultGenerator($chunks)); + ]; + $this->spannerClient->streamingRead( + Argument::type(ReadRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->connection->reveal()); + $serializer = $this->serializerForStreamingRead($chunks, $transaction); + $database = $this->database($this->spannerClient->reveal(), $serializer); $database->read('Table', new KeySet, [], [ 'begin' => true, 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE @@ -739,23 +890,34 @@ public function testDatabaseReadBeginReadWrite($chunks) public function testTransactionPreAllocatedRollback() { - $this->connection->beginTransaction(Argument::withEntry('transactionOptions', [ - 'readWrite' => [] - ]))->shouldBeCalledTimes(1)->willReturn(['id' => self::TRANSACTION]); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertEquals($request->getOptions(), $this->createTransactionOptions()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $sess = SpannerClient::sessionName( + $session = SpannerClient::sessionName( self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION ); + $this->spannerClient->rollback( + Argument::that(function ($request) use ($session) { + Argument::type(RollbackRequest::class); + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); + $this->assertEquals($request->getSession(), $session); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); - $this->connection->rollback(Argument::allOf( - Argument::withEntry('transactionId', self::TRANSACTION), - Argument::withEntry('session', $sess) - ))->shouldBeCalled(); - - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $t = $database->transaction(); $t->rollback(); } @@ -764,32 +926,146 @@ public function testTransactionSingleUseRollback() { $this->expectException(\BadMethodCallException::class); - $this->connection->beginTransaction(Argument::any())->shouldNotbeCalled(); - $this->connection->rollback(Argument::any())->shouldNotbeCalled(); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->connection->reveal()); + $database = $this->database($this->spannerClient->reveal()); $t = $database->transaction(['singleUse' => true]); $t->rollback(); } - private function database(ConnectionInterface $connection) + private function database(SpannerClient $spannerClient, Serializer $serializer = null) { - $operation = new Operation($connection, false); $instance = $this->prophesize(Instance::class); $instance->name()->willReturn(InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)); $instance->directedReadOptions()->willReturn([]); - $database = TestHelpers::stub(Database::class, [ - $connection, + $database = new Database( + $spannerClient, + $this->prophesize(DatabaseAdminClient::class)->reveal(), + $serializer ?: new Serializer(), $instance->reveal(), - $this->prophesize(LongRunningConnectionInterface::class)->reveal(), - [], self::PROJECT, self::DATABASE - ], ['operation']); - - $database->___setProperty('operation', $operation); + ); return $database; } + + private function serializerForStreamingRead(array $chunks, array $expectedTransaction): Serializer + { + // mock serializer responses for streaming read + $this->serializer->decodeMessage( + Argument::type(ReadRequest::class), + Argument::that(function ($data) use ($expectedTransaction) { + $this->assertEquals($data['transaction'], $expectedTransaction); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn(new ReadRequest()); + + foreach ($chunks as $chunk) { + $result = new PartialResultSet(); + $result->mergeFromJsonString($chunk); + $this->serializer->encodeMessage($result) + ->shouldBeCalledOnce() + ->willReturn(json_decode($chunk, true)); + } + + return $this->serializer->reveal(); + } + + private function serializerForStreamingSql(array $chunks, array $expectedTransaction): Serializer + { + // mock serializer responses for streaming read + $this->serializer->decodeMessage( + Argument::type(ExecuteSqlRequest::class), + Argument::that(function ($data) use ($expectedTransaction) { + $this->assertEquals($data['transaction'], $expectedTransaction); + return true; + }) + ) + ->shouldBeCalledOnce() + ->willReturn(new ExecuteSqlRequest()); + + foreach ($chunks as $chunk) { + $result = new PartialResultSet(); + $result->mergeFromJsonString($chunk); + $this->serializer->encodeMessage($result) + ->shouldBeCalledOnce() + ->willReturn(json_decode($chunk, true)); + } + + return $this->serializer->reveal(); + } + + private function getFullyQualifiedSessionName() + { + return SpannerClient::sessionName( + self::PROJECT, + self::INSTANCE, + self::DATABASE, + self::SESSION + ); + } + + private function createTransactionOptions($options = []) + { + $serializer = new Serializer(); + $transactionOptions = new TransactionOptions; + if (isset($options['readOnly'])) { + $readOnly = $serializer->decodeMessage( + new PBReadOnly(), + $options['readOnly'] + ); + $transactionOptions->setReadOnly($readOnly); + } else { + $readWrite = $readOnly = $serializer->decodeMessage( + new ReadWrite(), + $options['readOnly'] ?? [] + ); + $transactionOptions->setReadWrite($readWrite); + } + return $transactionOptions; + } + + private function createMockedCommitDatabase() + { + $this->spannerClient->commit( + Argument::that(function (CommitRequest $request) { + $this->assertEquals( + $request->getSingleUseTransaction(), + $this->createTransactionOptions() + ); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->protoTimestamp])); + + return $this->database($this->spannerClient->reveal()); + } + + // private function resultGeneratorStream(array $chunks) + // { + // foreach ($chunks as $i => $chunk) { + // $result = new PartialResultSet(); + // $result->mergeFromJsonString($chunk); + // $chunks[$i] = $result; + // } + // $this->stream = $this->prophesize(ServerStream::class); + // $this->stream->readAll() + // ->willReturn($this->resultGenerator($chunks)); + + // return $this->stream->reveal(); + // } + + // private function resultGenerator($chunks) + // { + // foreach ($chunks as $chunk) { + // yield $chunk; + // } + // } } diff --git a/Spanner/tests/Unit/V1/Client/SpannerClientTest.php b/Spanner/tests/Unit/V1/Client/SpannerClientTest.php index f013ee470349..7fadce9cf97d 100644 --- a/Spanner/tests/Unit/V1/Client/SpannerClientTest.php +++ b/Spanner/tests/Unit/V1/Client/SpannerClientTest.php @@ -24,6 +24,7 @@ use Google\ApiCore\ApiException; use Google\ApiCore\CredentialsWrapper; +use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; use Google\ApiCore\Testing\GeneratedTest; use Google\ApiCore\Testing\MockTransport; diff --git a/Spanner/tests/Unit/V1/SpannerClientPartialVeneerTest.php b/Spanner/tests/Unit/V1/SpannerClientPartialVeneerTest.php deleted file mode 100644 index ff77aaddce50..000000000000 --- a/Spanner/tests/Unit/V1/SpannerClientPartialVeneerTest.php +++ /dev/null @@ -1,45 +0,0 @@ - $this->getMockBuilder(CredentialsWrapper::class) - ->disableOriginalConstructor() - ->getMock(), - ]; - - $client = new SpannerClient($options + [ - 'transport' => new MockTransport(null) - ]); - - $this->assertInstanceOf(MockTransport::class, $client->getTransport()); - } -} diff --git a/Spanner/tests/Unit/V1/SpannerClientTest.php b/Spanner/tests/Unit/V1/SpannerClientTest.php deleted file mode 100644 index 1de21c55dcc9..000000000000 --- a/Spanner/tests/Unit/V1/SpannerClientTest.php +++ /dev/null @@ -1,1161 +0,0 @@ -getMockBuilder(CredentialsWrapper::class)->disableOriginalConstructor()->getMock(); - } - - /** @return SpannerClient */ - private function createClient(array $options = []) - { - $options += [ - 'credentials' => $this->createCredentials(), - ]; - return new SpannerClient($options); - } - - /** @test */ - public function batchCreateSessionsTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new BatchCreateSessionsResponse(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - $sessionCount = 185691686; - $response = $gapicClient->batchCreateSessions($formattedDatabase, $sessionCount); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/BatchCreateSessions', $actualFuncCall); - $actualValue = $actualRequestObject->getDatabase(); - $this->assertProtobufEquals($formattedDatabase, $actualValue); - $actualValue = $actualRequestObject->getSessionCount(); - $this->assertProtobufEquals($sessionCount, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function batchCreateSessionsExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - $sessionCount = 185691686; - try { - $gapicClient->batchCreateSessions($formattedDatabase, $sessionCount); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function batchWriteTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new BatchWriteResponse(); - $transport->addResponse($expectedResponse); - $expectedResponse2 = new BatchWriteResponse(); - $transport->addResponse($expectedResponse2); - $expectedResponse3 = new BatchWriteResponse(); - $transport->addResponse($expectedResponse3); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $mutationGroups = []; - $serverStream = $gapicClient->batchWrite($formattedSession, $mutationGroups); - $this->assertInstanceOf(ServerStream::class, $serverStream); - $responses = iterator_to_array($serverStream->readAll()); - $expectedResponses = []; - $expectedResponses[] = $expectedResponse; - $expectedResponses[] = $expectedResponse2; - $expectedResponses[] = $expectedResponse3; - $this->assertEquals($expectedResponses, $responses); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/BatchWrite', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getMutationGroups(); - $this->assertProtobufEquals($mutationGroups, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function batchWriteExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->setStreamingStatus($status); - $this->assertTrue($transport->isExhausted()); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $mutationGroups = []; - $serverStream = $gapicClient->batchWrite($formattedSession, $mutationGroups); - $results = $serverStream->readAll(); - try { - iterator_to_array($results); - // If the close stream method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function beginTransactionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $id = '27'; - $expectedResponse = new Transaction(); - $expectedResponse->setId($id); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $options = new TransactionOptions(); - $response = $gapicClient->beginTransaction($formattedSession, $options); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/BeginTransaction', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getOptions(); - $this->assertProtobufEquals($options, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function beginTransactionExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $options = new TransactionOptions(); - try { - $gapicClient->beginTransaction($formattedSession, $options); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function commitTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new CommitResponse(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $mutations = []; - $response = $gapicClient->commit($formattedSession, $mutations); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/Commit', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getMutations(); - $this->assertProtobufEquals($mutations, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function commitExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $mutations = []; - try { - $gapicClient->commit($formattedSession, $mutations); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function createSessionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $name = 'name3373707'; - $creatorRole = 'creatorRole-1605962583'; - $multiplexed = false; - $expectedResponse = new Session(); - $expectedResponse->setName($name); - $expectedResponse->setCreatorRole($creatorRole); - $expectedResponse->setMultiplexed($multiplexed); - $transport->addResponse($expectedResponse); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - $response = $gapicClient->createSession($formattedDatabase); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/CreateSession', $actualFuncCall); - $actualValue = $actualRequestObject->getDatabase(); - $this->assertProtobufEquals($formattedDatabase, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function createSessionExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - try { - $gapicClient->createSession($formattedDatabase); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function deleteSessionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new GPBEmpty(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedName = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $gapicClient->deleteSession($formattedName); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/DeleteSession', $actualFuncCall); - $actualValue = $actualRequestObject->getName(); - $this->assertProtobufEquals($formattedName, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function deleteSessionExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedName = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - try { - $gapicClient->deleteSession($formattedName); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeBatchDmlTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new ExecuteBatchDmlResponse(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $transaction = new TransactionSelector(); - $statements = []; - $seqno = 109325920; - $response = $gapicClient->executeBatchDml($formattedSession, $transaction, $statements, $seqno); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/ExecuteBatchDml', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getTransaction(); - $this->assertProtobufEquals($transaction, $actualValue); - $actualValue = $actualRequestObject->getStatements(); - $this->assertProtobufEquals($statements, $actualValue); - $actualValue = $actualRequestObject->getSeqno(); - $this->assertProtobufEquals($seqno, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeBatchDmlExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $transaction = new TransactionSelector(); - $statements = []; - $seqno = 109325920; - try { - $gapicClient->executeBatchDml($formattedSession, $transaction, $statements, $seqno); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeSqlTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new ResultSet(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - $response = $gapicClient->executeSql($formattedSession, $sql); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/ExecuteSql', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getSql(); - $this->assertProtobufEquals($sql, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeSqlExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - try { - $gapicClient->executeSql($formattedSession, $sql); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeStreamingSqlTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $chunkedValue = true; - $resumeToken2 = '90'; - $expectedResponse = new PartialResultSet(); - $expectedResponse->setChunkedValue($chunkedValue); - $expectedResponse->setResumeToken($resumeToken2); - $transport->addResponse($expectedResponse); - $chunkedValue2 = false; - $resumeToken3 = '91'; - $expectedResponse2 = new PartialResultSet(); - $expectedResponse2->setChunkedValue($chunkedValue2); - $expectedResponse2->setResumeToken($resumeToken3); - $transport->addResponse($expectedResponse2); - $chunkedValue3 = true; - $resumeToken4 = '92'; - $expectedResponse3 = new PartialResultSet(); - $expectedResponse3->setChunkedValue($chunkedValue3); - $expectedResponse3->setResumeToken($resumeToken4); - $transport->addResponse($expectedResponse3); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - $serverStream = $gapicClient->executeStreamingSql($formattedSession, $sql); - $this->assertInstanceOf(ServerStream::class, $serverStream); - $responses = iterator_to_array($serverStream->readAll()); - $expectedResponses = []; - $expectedResponses[] = $expectedResponse; - $expectedResponses[] = $expectedResponse2; - $expectedResponses[] = $expectedResponse3; - $this->assertEquals($expectedResponses, $responses); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/ExecuteStreamingSql', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getSql(); - $this->assertProtobufEquals($sql, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function executeStreamingSqlExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->setStreamingStatus($status); - $this->assertTrue($transport->isExhausted()); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - $serverStream = $gapicClient->executeStreamingSql($formattedSession, $sql); - $results = $serverStream->readAll(); - try { - iterator_to_array($results); - // If the close stream method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function getSessionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $name2 = 'name2-1052831874'; - $creatorRole = 'creatorRole-1605962583'; - $multiplexed = false; - $expectedResponse = new Session(); - $expectedResponse->setName($name2); - $expectedResponse->setCreatorRole($creatorRole); - $expectedResponse->setMultiplexed($multiplexed); - $transport->addResponse($expectedResponse); - // Mock request - $formattedName = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $response = $gapicClient->getSession($formattedName); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/GetSession', $actualFuncCall); - $actualValue = $actualRequestObject->getName(); - $this->assertProtobufEquals($formattedName, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function getSessionExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedName = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - try { - $gapicClient->getSession($formattedName); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function listSessionsTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $nextPageToken = ''; - $sessionsElement = new Session(); - $sessions = [ - $sessionsElement, - ]; - $expectedResponse = new ListSessionsResponse(); - $expectedResponse->setNextPageToken($nextPageToken); - $expectedResponse->setSessions($sessions); - $transport->addResponse($expectedResponse); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - $response = $gapicClient->listSessions($formattedDatabase); - $this->assertEquals($expectedResponse, $response->getPage()->getResponseObject()); - $resources = iterator_to_array($response->iterateAllElements()); - $this->assertSame(1, count($resources)); - $this->assertEquals($expectedResponse->getSessions()[0], $resources[0]); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/ListSessions', $actualFuncCall); - $actualValue = $actualRequestObject->getDatabase(); - $this->assertProtobufEquals($formattedDatabase, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function listSessionsExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedDatabase = $gapicClient->databaseName('[PROJECT]', '[INSTANCE]', '[DATABASE]'); - try { - $gapicClient->listSessions($formattedDatabase); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function partitionQueryTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new PartitionResponse(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - $response = $gapicClient->partitionQuery($formattedSession, $sql); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/PartitionQuery', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getSql(); - $this->assertProtobufEquals($sql, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function partitionQueryExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $sql = 'sql114126'; - try { - $gapicClient->partitionQuery($formattedSession, $sql); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function partitionReadTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new PartitionResponse(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $keySet = new KeySet(); - $response = $gapicClient->partitionRead($formattedSession, $table, $keySet); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/PartitionRead', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getTable(); - $this->assertProtobufEquals($table, $actualValue); - $actualValue = $actualRequestObject->getKeySet(); - $this->assertProtobufEquals($keySet, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function partitionReadExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $keySet = new KeySet(); - try { - $gapicClient->partitionRead($formattedSession, $table, $keySet); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function readTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new ResultSet(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $columns = []; - $keySet = new KeySet(); - $response = $gapicClient->read($formattedSession, $table, $columns, $keySet); - $this->assertEquals($expectedResponse, $response); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/Read', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getTable(); - $this->assertProtobufEquals($table, $actualValue); - $actualValue = $actualRequestObject->getColumns(); - $this->assertProtobufEquals($columns, $actualValue); - $actualValue = $actualRequestObject->getKeySet(); - $this->assertProtobufEquals($keySet, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function readExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $columns = []; - $keySet = new KeySet(); - try { - $gapicClient->read($formattedSession, $table, $columns, $keySet); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function rollbackTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $expectedResponse = new GPBEmpty(); - $transport->addResponse($expectedResponse); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $transactionId = '28'; - $gapicClient->rollback($formattedSession, $transactionId); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/Rollback', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getTransactionId(); - $this->assertProtobufEquals($transactionId, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function rollbackExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->addResponse(null, $status); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $transactionId = '28'; - try { - $gapicClient->rollback($formattedSession, $transactionId); - // If the $gapicClient method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function streamingReadTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $this->assertTrue($transport->isExhausted()); - // Mock response - $chunkedValue = true; - $resumeToken2 = '90'; - $expectedResponse = new PartialResultSet(); - $expectedResponse->setChunkedValue($chunkedValue); - $expectedResponse->setResumeToken($resumeToken2); - $transport->addResponse($expectedResponse); - $chunkedValue2 = false; - $resumeToken3 = '91'; - $expectedResponse2 = new PartialResultSet(); - $expectedResponse2->setChunkedValue($chunkedValue2); - $expectedResponse2->setResumeToken($resumeToken3); - $transport->addResponse($expectedResponse2); - $chunkedValue3 = true; - $resumeToken4 = '92'; - $expectedResponse3 = new PartialResultSet(); - $expectedResponse3->setChunkedValue($chunkedValue3); - $expectedResponse3->setResumeToken($resumeToken4); - $transport->addResponse($expectedResponse3); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $columns = []; - $keySet = new KeySet(); - $serverStream = $gapicClient->streamingRead($formattedSession, $table, $columns, $keySet); - $this->assertInstanceOf(ServerStream::class, $serverStream); - $responses = iterator_to_array($serverStream->readAll()); - $expectedResponses = []; - $expectedResponses[] = $expectedResponse; - $expectedResponses[] = $expectedResponse2; - $expectedResponses[] = $expectedResponse3; - $this->assertEquals($expectedResponses, $responses); - $actualRequests = $transport->popReceivedCalls(); - $this->assertSame(1, count($actualRequests)); - $actualFuncCall = $actualRequests[0]->getFuncCall(); - $actualRequestObject = $actualRequests[0]->getRequestObject(); - $this->assertSame('/google.spanner.v1.Spanner/StreamingRead', $actualFuncCall); - $actualValue = $actualRequestObject->getSession(); - $this->assertProtobufEquals($formattedSession, $actualValue); - $actualValue = $actualRequestObject->getTable(); - $this->assertProtobufEquals($table, $actualValue); - $actualValue = $actualRequestObject->getColumns(); - $this->assertProtobufEquals($columns, $actualValue); - $actualValue = $actualRequestObject->getKeySet(); - $this->assertProtobufEquals($keySet, $actualValue); - $this->assertTrue($transport->isExhausted()); - } - - /** @test */ - public function streamingReadExceptionTest() - { - $transport = $this->createTransport(); - $gapicClient = $this->createClient([ - 'transport' => $transport, - ]); - $status = new stdClass(); - $status->code = Code::DATA_LOSS; - $status->details = 'internal error'; - $expectedExceptionMessage = json_encode([ - 'message' => 'internal error', - 'code' => Code::DATA_LOSS, - 'status' => 'DATA_LOSS', - 'details' => [], - ], JSON_PRETTY_PRINT); - $transport->setStreamingStatus($status); - $this->assertTrue($transport->isExhausted()); - // Mock request - $formattedSession = $gapicClient->sessionName('[PROJECT]', '[INSTANCE]', '[DATABASE]', '[SESSION]'); - $table = 'table110115790'; - $columns = []; - $keySet = new KeySet(); - $serverStream = $gapicClient->streamingRead($formattedSession, $table, $columns, $keySet); - $results = $serverStream->readAll(); - try { - iterator_to_array($results); - // If the close stream method call did not throw, fail the test - $this->fail('Expected an ApiException, but no exception was thrown.'); - } catch (ApiException $ex) { - $this->assertEquals($status->code, $ex->getCode()); - $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); - } - // Call popReceivedCalls to ensure the stub is exhausted - $transport->popReceivedCalls(); - $this->assertTrue($transport->isExhausted()); - } -} diff --git a/Spanner/tests/Unit/bootstrap.php b/Spanner/tests/Unit/bootstrap.php new file mode 100644 index 000000000000..f16f16a2c9c5 --- /dev/null +++ b/Spanner/tests/Unit/bootstrap.php @@ -0,0 +1,12 @@ + Date: Mon, 11 Nov 2024 20:58:29 -0800 Subject: [PATCH 2/8] fixes from PR review --- Core/src/RequestHandler.php | 2 +- .../LongRunningOperationManagerTest.php | 420 ------------------ .../OperationResponseTraitTest.php | 3 - Spanner/src/Backup.php | 7 +- Spanner/src/Batch/BatchSnapshot.php | 1 - Spanner/src/Batch/QueryPartition.php | 2 - Spanner/src/Database.php | 35 +- Spanner/src/FormatKeySetTrait.php | 37 ++ Spanner/src/Instance.php | 9 +- Spanner/src/InstanceConfiguration.php | 4 +- Spanner/src/KeySet.php | 1 - Spanner/src/MutationTrait.php | 5 +- Spanner/src/Operation.php | 34 +- Spanner/src/RequestTrait.php | 15 - Spanner/src/Session/CacheSessionPool.php | 8 +- Spanner/src/Session/Session.php | 7 +- Spanner/src/Snapshot.php | 3 +- Spanner/src/SnapshotTrait.php | 1 - Spanner/src/SpannerClient.php | 13 +- Spanner/src/Transaction.php | 5 +- Spanner/src/TransactionConfigurationTrait.php | 2 +- Spanner/src/V1/SpannerClient.php | 52 --- Spanner/src/ValueMapper.php | 15 +- Spanner/tests/Perf/ycsb.php | 2 +- Spanner/tests/ResultGeneratorTrait.php | 5 +- Spanner/tests/Snippet/ArrayTypeTest.php | 4 +- Spanner/tests/Snippet/BackupTest.php | 9 +- .../tests/Snippet/Batch/BatchClientTest.php | 2 +- .../tests/Snippet/Batch/BatchSnapshotTest.php | 5 +- .../Batch/PartitionSharedSnippetTestTrait.php | 2 +- .../Snippet/Batch/QueryPartitionTest.php | 2 - .../tests/Snippet/Batch/ReadPartitionTest.php | 2 - Spanner/tests/Snippet/BatchDmlResultTest.php | 3 +- Spanner/tests/Snippet/BytesTest.php | 4 +- Spanner/tests/Snippet/CommitTimestampTest.php | 2 - Spanner/tests/Snippet/DatabaseTest.php | 34 +- Spanner/tests/Snippet/DateTest.php | 4 +- .../Snippet/InstanceConfigurationTest.php | 5 +- Spanner/tests/Snippet/InstanceTest.php | 36 +- Spanner/tests/Snippet/KeyRangeTest.php | 4 +- Spanner/tests/Snippet/KeySetTest.php | 2 +- Spanner/tests/Snippet/NumericTest.php | 1 - Spanner/tests/Snippet/ResultTest.php | 2 +- .../Snippet/Session/CacheSessionPoolTest.php | 6 +- Spanner/tests/Snippet/SnapshotTest.php | 2 +- Spanner/tests/Snippet/SpannerClientTest.php | 13 +- Spanner/tests/Snippet/StructTypeTest.php | 13 +- Spanner/tests/Snippet/StructValueTest.php | 5 +- Spanner/tests/Snippet/TimestampTest.php | 6 +- Spanner/tests/Snippet/TransactionTest.php | 34 +- .../Snippet/TransactionalReadMethodsTest.php | 2 +- Spanner/tests/System/AdminTest.php | 8 +- Spanner/tests/System/BackupTest.php | 31 +- Spanner/tests/System/BatchTest.php | 6 +- Spanner/tests/System/BatchWriteTest.php | 6 +- .../System/GeneratedAdminEmulatorTest.php | 2 +- Spanner/tests/System/LargeReadTest.php | 8 +- Spanner/tests/System/OperationsTest.php | 8 +- Spanner/tests/System/PartitionedDmlTest.php | 2 - Spanner/tests/System/PgBatchTest.php | 6 +- Spanner/tests/System/PgBatchWriteTest.php | 6 +- Spanner/tests/System/PgPartitionedDmlTest.php | 2 - Spanner/tests/System/PgQueryTest.php | 22 +- Spanner/tests/System/PgReadTest.php | 4 +- Spanner/tests/System/PgTransactionTest.php | 2 +- Spanner/tests/System/PgWriteTest.php | 58 +-- Spanner/tests/System/QueryTest.php | 74 +-- Spanner/tests/System/SessionTest.php | 4 +- Spanner/tests/System/SnapshotTest.php | 8 +- Spanner/tests/System/SpannerPgTestCase.php | 10 +- Spanner/tests/System/SpannerTestCase.php | 12 +- Spanner/tests/System/TransactionTest.php | 9 +- Spanner/tests/System/WriteTest.php | 54 +-- .../System/pcntl/AbortedErrorCausesRetry.php | 2 +- ...tTransactionsIncrementValueWithExecute.php | 4 +- ...rentTransactionsIncrementValueWithRead.php | 6 +- Spanner/tests/Unit/ArrayTypeTest.php | 2 +- Spanner/tests/Unit/BackupTest.php | 11 +- Spanner/tests/Unit/Batch/BatchClientTest.php | 14 +- .../tests/Unit/Batch/BatchSnapshotTest.php | 22 +- Spanner/tests/Unit/BytesTest.php | 2 +- Spanner/tests/Unit/CommitTimestampTest.php | 2 +- Spanner/tests/Unit/DatabaseTest.php | 297 ++++++------- Spanner/tests/Unit/DateTest.php | 4 +- .../tests/Unit/InstanceConfigurationTest.php | 7 +- Spanner/tests/Unit/InstanceTest.php | 33 +- Spanner/tests/Unit/KeyRangeTest.php | 4 +- Spanner/tests/Unit/KeySetTest.php | 18 +- Spanner/tests/Unit/OperationTest.php | 51 +-- Spanner/tests/Unit/PgJsonbTest.php | 4 +- Spanner/tests/Unit/PgNumericTest.php | 2 +- Spanner/tests/Unit/ResultTest.php | 11 +- .../Unit/Session/CacheSessionPoolTest.php | 18 +- Spanner/tests/Unit/SnapshotTest.php | 8 +- Spanner/tests/Unit/SpannerClientTest.php | 24 +- Spanner/tests/Unit/StructTypeTest.php | 14 +- Spanner/tests/Unit/StructValueTest.php | 4 +- Spanner/tests/Unit/TimestampTest.php | 4 +- .../TransactionConfigurationTraitTest.php | 2 +- Spanner/tests/Unit/TransactionTest.php | 13 +- Spanner/tests/Unit/TransactionTypeTest.php | 27 +- .../Unit/V1/Client/SpannerClientTest.php | 1 - Spanner/tests/Unit/ValueMapperTest.php | 32 +- 103 files changed, 655 insertions(+), 1222 deletions(-) delete mode 100644 Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php create mode 100644 Spanner/src/FormatKeySetTrait.php delete mode 100644 Spanner/src/V1/SpannerClient.php diff --git a/Core/src/RequestHandler.php b/Core/src/RequestHandler.php index f66e026e672e..7fff6befc8eb 100644 --- a/Core/src/RequestHandler.php +++ b/Core/src/RequestHandler.php @@ -138,7 +138,7 @@ public function sendRequest( * @param $clientClass The client class whose object we need. * @return mixed */ - public function getClientObject(string $clientClass) + private function getClientObject(string $clientClass) { return $this->clients[$clientClass] ?? null; } diff --git a/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php b/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php deleted file mode 100644 index 012ce33d9335..000000000000 --- a/Core/tests/Snippet/LongRunning/LongRunningOperationManagerTest.php +++ /dev/null @@ -1,420 +0,0 @@ - self::METADATA_TYPE, - 'message' => RequestMetadata::class, - ], [ - 'typeUrl' => self::RESULT_TYPE, - 'message' => AuthorizationInfo::class, - ], - ]; - - public function setUp(): void - { - $this->requestHandler = $this->prophesize(RequestHandler::class); - $serializer = $this->prophesize(Serializer::class); - $serializer->encodeMessage(Argument::any())->will(function ($arg) { - if (is_bool($arg[0]) || is_array($arg[0])) { - return $arg[0]; - } - if (method_exists($arg[0], 'serializeToJsonString')) { - $json = $arg[0]->serializeToJsonString(); - return json_decode($json, true); - } - }); - $this->serializer = $serializer->reveal(); - $this->callables = [ - [ - 'typeUrl' => self::TYPE, - 'callable' => function ($res) { - return $res; - } - ] - ]; - $this->operation = TestHelpers::stub(LongRunningOperationManager::class, [ - $this->requestHandler->reveal(), - $this->serializer, - $this->callables, - $this->lroResponseMappers, - DatabaseAdminClient::class, - self::NAME, ); - } - - public function testName() - { - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'name'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('name'); - $this->assertEquals(self::NAME, $res->returnVal()); - } - - public function testDone() - { - $this->mockResumeOperation(); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'done'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke(); - $this->assertEquals('The operation is done!', $res->output()); - } - - public function testStateInProgress() - { - $this->mockResumeOperation(false); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); - $snippet->addLocal('operation', $this->operation); - $snippet->addUse(LongRunningOperationManager::class); - - $res = $snippet->invoke(); - $this->assertEquals('Operation is in progress', $res->output()); - } - - public function testStateDone() - { - $this->mockResumeOperation(); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); - $snippet->addUse(LongRunningOperationManager::class); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke(); - $this->assertEquals('Operation succeeded', $res->output()); - } - - public function testStateFailed() - { - $this->mockResumeOperation(true, false); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'state'); - $snippet->addLocal('operation', $this->operation); - $snippet->addUse(LongRunningOperationManager::class); - - $res = $snippet->invoke(); - $this->assertEquals('Operation failed', $res->output()); - } - - public function testResult() - { - $result = [ - 'resource' => 'any', - 'permission' => 'all', - 'granted' => true, - ]; - $this->mockResumeOperation(); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'result'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('result'); - $this->assertEquals($result, $res->returnVal()); - } - - public function testError() - { - $result = [ - 'foo' => 'bar' - ]; - $this->mockResumeOperation(true, false, true); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'error'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('error'); - $this->assertEquals($result, $res->returnVal()); - } - - public function testInfo() - { - $result = [ - 'resource' => 'any', - 'permission' => 'all', - 'granted' => true, - ]; - $this->mockResumeOperation(); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'info'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('info'); - $this->assertEquals($result, $res->returnVal()['response']); - - $snippet->invoke(); - } - - public function testReload() - { - $result = [ - 'resource' => 'any', - 'permission' => 'all', - 'granted' => true, - ]; - $this->mockResumeOperation(true, true, false, 2); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'reload'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('result'); - $this->assertEquals($result, $res->returnVal()['response']); - - $snippet->invoke(); - } - - public function testPollUntilComplete() - { - $iteration = 0; - $result = [ - 'resource' => 'any', - 'permission' => 'all', - 'granted' => true, - ]; - $incompleteOp = $this->getOperationResponseMock(false); - $completeOp = $this->getOperationResponseMock(); - $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); - $this->requestHandler->getClientObject(Argument::any()) - ->shouldBeCalled() - ->willReturn($databaseAdminClient); - $databaseAdminClient->resumeOperation(Argument::that(function ($arg) use (&$iteration) { - $iteration++; - return $iteration == 1; - }), Argument::any())->willReturn($incompleteOp); - $databaseAdminClient->resumeOperation(Argument::that(function ($arg) use (&$iteration) { - return $iteration != 1; - }), Argument::any())->willReturn($completeOp); - - $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->operation->___setProperty('serializer', $this->serializer); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'pollUntilComplete'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke('result'); - $this->assertEquals($result, $res->returnVal()); - } - - public function testCancel() - { - $this->checkAndSkipTest([ - DatabaseAdminClient::class, - ]); - $operation = $this->prophesize(OperationResponse::class); - $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); - $operation->cancel(Argument::any())->shouldBeCalled(); - $this->requestHandler->getClientObject(Argument::any()) - ->shouldBeCalled() - ->willReturn($databaseAdminClient); - $databaseAdminClient->resumeOperation(Argument::cetera()) - ->willReturn($operation->reveal()); - - $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->operation->___setProperty('serializer', $this->serializer); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'cancel'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke(); - } - - public function testDelete() - { - $this->checkAndSkipTest([ - DatabaseAdminClient::class, - ]); - $operation = $this->prophesize(OperationResponse::class); - $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); - $operation->delete(Argument::any())->shouldBeCalled(); - $this->requestHandler->getClientObject(Argument::any()) - ->shouldBeCalled() - ->willReturn($databaseAdminClient); - $databaseAdminClient->resumeOperation(Argument::cetera()) - ->willReturn($operation->reveal()); - - $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->operation->___setProperty('serializer', $this->serializer); - - $snippet = $this->snippetFromMethod(LongRunningOperationManager::class, 'delete'); - $snippet->addLocal('operation', $this->operation); - - $res = $snippet->invoke(); - } - - private function mockResumeOperation( - bool $done = true, - bool $result = true, - bool $error = false, - int $times = 1 - ) { - $operation = $this->getOperationResponseMock($done, $result, $error); - $databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); - $this->requestHandler->getClientObject(Argument::any()) - ->shouldBeCalledTimes($times) - ->willReturn($databaseAdminClient); - $databaseAdminClient->resumeOperation(Argument::cetera())->willReturn($operation); - - $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->operation->___setProperty('serializer', $this->serializer); - } - - private function getOperationResponseMock( - bool $done = true, - bool $result = true, - bool $error = false, - int $times = 1 - ) { - $this->checkAndSkipTest([ - DatabaseAdminClient::class, - ]); - if ($result) { - $result = new AuthorizationInfo([ - 'resource' => 'any', - 'permission' => 'all', - 'granted' => true, - ]); - } - if ($error) { - $error = ['foo' => 'bar']; - } - $meta = new RequestMetadata([ - 'caller_ip' => '127.8.9.10', // Sic(!) - ]); - $response = new Response(self::METADATA_TYPE, $meta, $done, self::RESULT_TYPE, $result, $error); - $operation = new OperationResponse(self::OPERATION_NAME, null, ['lastProtoResponse' => $response]); - return $operation; - } -} - -//@codingStandardsIgnoreStart - -class Value -{ - public $value; - - public function __construct($value = null) - { - $this->value = $value; - } - - public function getValue() - { - return $this->value; - } -} - -class Response -{ - private $metadataType; - private $metadata; - private $responseType; - private $response = null; - private $error = null; - private $done = true; - - public function __construct( - $metaType, - $metadata, - $done = true, - $respType = null, - $response = null, - $error = null - ) { - $this->metadataType = $metaType; - $this->metadata = $metadata->serializeToString(); - $this->responseType = $respType; - $this->done = $done; - if ($response) { - $this->response = $response->serializeToString(); - } - $this->error = $error; - } - - public function getResponse() - { - return new Value($this->response); - } - - public function getMetadata() - { - return new Value($this->metadata); - } - - public function getDone() - { - return (isset($this->response) or isset($this->error)); - } - - public function getError() - { - return $this->error; - } - - public function serializeToJsonString() - { - $result = [ - 'done' => $this->done, - 'metadata' => [ - 'typeUrl' => $this->metadataType, - 'value' => $this->metadata, - ], - ]; - if (isset($this->response)) { - $result['response'] = [ - 'typeUrl' => $this->responseType, - 'value' => $this->response, - ]; - } - - return json_encode($result); - } -} diff --git a/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php b/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php index 21ee93ac7d5d..dcd57f9cbdc8 100644 --- a/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php +++ b/Core/tests/Unit/LongRunning/OperationResponseTraitTest.php @@ -21,11 +21,9 @@ use Google\ApiCore\Serializer; use Google\Cloud\Core\LongRunning\OperationResponseTrait; use Google\Cloud\Core\LongRunning\LongRunningOperation; -use Google\Cloud\Core\LongRunning\LongRunningOperationManager; use Google\Cloud\Core\LongRunning\LongRunningConnectionInterface; use Google\Cloud\Core\RequestHandler; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Core\Testing\CheckForClassTrait; use Prophecy\Argument; use Google\Cloud\Audit\RequestMetadata; use Google\Cloud\Audit\AuthorizationInfo; @@ -38,7 +36,6 @@ */ class OperationResponseTraitTest extends TestCase { - use CheckForClassTrait; use ProphecyTrait; use OperationResponseTrait; diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index fe231bea5095..44e300a271e1 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -18,20 +18,21 @@ namespace Google\Cloud\Spanner; use Closure; +use DateTimeInterface; +use Google\ApiCore\OperationResponse; use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Core\Exception\NotFoundException; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Backup\State; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; -use DateTimeInterface; /** * Represents a Cloud Spanner Backup. diff --git a/Spanner/src/Batch/BatchSnapshot.php b/Spanner/src/Batch/BatchSnapshot.php index def2870f697d..258660a382cb 100644 --- a/Spanner/src/Batch/BatchSnapshot.php +++ b/Spanner/src/Batch/BatchSnapshot.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner\Batch; -use Google\ApiCore\Serializer; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; diff --git a/Spanner/src/Batch/QueryPartition.php b/Spanner/src/Batch/QueryPartition.php index acdcc3a684f9..a29868007b57 100644 --- a/Spanner/src/Batch/QueryPartition.php +++ b/Spanner/src/Batch/QueryPartition.php @@ -17,8 +17,6 @@ namespace Google\Cloud\Spanner\Batch; -use Google\Cloud\Spanner\Session\Session; - /** * Represents a Query Partition. * diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index 255b4d6229f7..d0c9df578a4e 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -18,10 +18,10 @@ namespace Google\Cloud\Spanner; use Closure; -use Google\ApiCore\RetrySettings; -use Google\ApiCore\Serializer; use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; +use Google\ApiCore\RetrySettings; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; @@ -30,9 +30,9 @@ use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; -use Google\Cloud\Core\Retry; use Google\Cloud\Core\RequestHandler; use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Core\Retry; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest; use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; @@ -46,10 +46,8 @@ use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest; -use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\V1\BatchCreateSessionsRequest; use Google\Cloud\Spanner\V1\BatchWriteRequest; use Google\Cloud\Spanner\V1\Client\SpannerClient; @@ -57,7 +55,6 @@ use Google\Cloud\Spanner\V1\Mutation; use Google\Cloud\Spanner\V1\Mutation\Delete; use Google\Cloud\Spanner\V1\Mutation\Write; -use Google\Cloud\Spanner\V1\BatchWriteResponse; use Google\Cloud\Spanner\V1\TypeCode; use Google\Protobuf\Duration; use Google\Protobuf\ListValue; @@ -94,6 +91,7 @@ class Database use RequestTrait; use RequestProcessorTrait; use ApiHelperTrait; + use FormatKeySetTrait; const STATE_CREATING = State::CREATING; const STATE_READY = State::READY; @@ -273,7 +271,7 @@ public function state(array $options = []) */ public function backups(array $options = []) { - $filter = "database:" . $this->name(); + $filter = 'database:' . $this->name(); if (isset($options['filter'])) { $filter = sprintf('(%1$s) AND (%2$s)', $filter, $this->pluck('filter', $options)); @@ -1776,7 +1774,7 @@ public function batchWrite(array $mutationGroups, array $options = []) array_walk( $mutationGroups, - fn(&$x) => $x['mutations'] = $this->parseMutations($x['mutations']) + fn (&$x) => $x['mutations'] = $this->parseMutations($x['mutations']) ); try { @@ -1928,6 +1926,7 @@ public function executePartitionedUpdate($statement, array $options = []) if (isset($options['transactionOptions']['excludeTxnFromChangeStreams'])) { $beginTransactionOptions['transactionOptions']['excludeTxnFromChangeStreams'] = $options['transactionOptions']['excludeTxnFromChangeStreams']; + unset($options['transactionOptions']); } $transaction = $this->operation->transaction($session, $beginTransactionOptions); @@ -2137,7 +2136,8 @@ public function __destruct() $this->close(); //@codingStandardsIgnoreStart //@codeCoverageIgnoreStart - } catch (\Exception $ex) {} + } catch (\Exception $ex) { + } //@codeCoverageIgnoreEnd //@codingStandardsIgnoreStart } @@ -2205,7 +2205,6 @@ public function batchCreateSessions(array $options) list($data, $callOptions) = $this->splitOptionalArgs($options); $data['database'] = $this->name; - $request = $this->serializer->decodeMessage(new BatchCreateSessionsRequest(), $data); $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); @@ -2230,7 +2229,7 @@ public function deleteSessionAsync(array $options) $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $response = $this->spannerClient->deleteSessionAsync($request, $callOptions); + return $this->spannerClient->deleteSessionAsync($request, $callOptions); } /** @@ -2516,12 +2515,12 @@ private function parseMutations($rawMutations) } $operation = $this->serializer->decodeMessage( - new Delete, + new Delete(), $data ); break; default: - $operation = new Write; + $operation = new Write(); $operation->setTable($data['table']); $operation->setColumns($data['columns']); @@ -2530,7 +2529,7 @@ private function parseMutations($rawMutations) $modifiedData[$key] = $this->fieldValue($param); } - $list = new ListValue; + $list = new ListValue(); $list->setValues($modifiedData); $values = [$list]; $operation->setValues($values); @@ -2539,7 +2538,7 @@ private function parseMutations($rawMutations) } $setterName = $this->mutationSetters[$type]; - $mutation = new Mutation; + $mutation = new Mutation(); $mutation->$setterName($operation); $mutations[] = $mutation; } @@ -2552,7 +2551,7 @@ private function parseMutations($rawMutations) */ private function fieldValue($param) { - $field = new Value; + $field = new Value(); $value = $this->formatValueForApi($param); $setter = null; @@ -2575,7 +2574,7 @@ private function fieldValue($param) foreach ($param as $key => $value) { $modifiedParams[$key] = $this->fieldValue($value); } - $value = new Struct; + $value = new Struct(); $value->setFields($modifiedParams); break; @@ -2585,7 +2584,7 @@ private function fieldValue($param) foreach ($param as $item) { $modifiedParams[] = $this->fieldValue($item); } - $list = new ListValue; + $list = new ListValue(); $list->setValues($modifiedParams); $value = $list; diff --git a/Spanner/src/FormatKeySetTrait.php b/Spanner/src/FormatKeySetTrait.php new file mode 100644 index 000000000000..cc1f1ef4b9b6 --- /dev/null +++ b/Spanner/src/FormatKeySetTrait.php @@ -0,0 +1,37 @@ +pluck('keys', $keySet, false); + if ($keys) { + $keySet['keys'] = array_map( + fn ($key) => $this->formatListForApi((array) $key), + $keys + ); + } + + if (isset($keySet['ranges'])) { + $keySet['ranges'] = array_map(function ($rangeItem) { + return array_map([$this, 'formatListForApi'], $rangeItem); + }, $keySet['ranges']); + + if (empty($keySet['ranges'])) { + unset($keySet['ranges']); + } + } + + return $keySet; + } +} \ No newline at end of file diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 7774173a66e4..80698e573a47 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -19,8 +19,8 @@ use Closure; use Google\ApiCore\ArrayTrait; +use Google\ApiCore\OperationResponse; use Google\ApiCore\Serializer; -use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; @@ -38,7 +38,6 @@ use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; use Google\Cloud\Spanner\Admin\Instance\V1\Instance\State; use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceRequest; -use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; @@ -296,7 +295,7 @@ public function create(InstanceConfiguration $config, array $options = []) list($instance, $callOptions) = $this->splitOptionalArgs($options); $instanceId = InstanceAdminClient::parseName($this->name)['instance']; if (isset($instance['nodeCount']) && isset($instance['processingUnits'])) { - throw new \InvalidArgumentException("Must only set either `nodeCount` or `processingUnits`"); + throw new \InvalidArgumentException('Must only set either `nodeCount` or `processingUnits`'); } if (empty($instance['nodeCount']) && empty($instance['processingUnits'])) { $instance['nodeCount'] = self::DEFAULT_NODE_COUNT; @@ -380,7 +379,7 @@ public function update(array $options = []) list($instance, $callOptions) = $this->splitOptionalArgs($options); if (isset($options['nodeCount']) && isset($options['processingUnits'])) { - throw new \InvalidArgumentException("Must only set either `nodeCount` or `processingUnits`"); + throw new \InvalidArgumentException('Must only set either `nodeCount` or `processingUnits`'); } $fieldMask = $this->fieldMask($instance); @@ -854,7 +853,7 @@ private function instanceResultFunction(): Closure $this->projectId, $name['instance'], $this->returnInt64AsObject, - $this->serialize->encodeMessage($result), + $this->serializer->encodeMessage($result), [ 'directedReadOptions' => $this->directedReadOptions, 'routeToLeader' => $this->routeToLeader, diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index 2a010ae2748c..7b0324ccadcf 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -17,9 +17,10 @@ namespace Google\Cloud\Spanner; +use Closure; use Google\ApiCore\ApiException; -use Google\ApiCore\Serializer; use Google\ApiCore\OperationResponse; +use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\RequestProcessorTrait; @@ -32,7 +33,6 @@ use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; use Google\Rpc\Code; -use Closure; /** * Represents a Cloud Spanner Instance Configuration. diff --git a/Spanner/src/KeySet.php b/Spanner/src/KeySet.php index 4aebb92eb526..04a646e9c7dc 100644 --- a/Spanner/src/KeySet.php +++ b/Spanner/src/KeySet.php @@ -102,7 +102,6 @@ public function ranges() return $this->ranges; } - /** * Add a single KeyRange. * diff --git a/Spanner/src/MutationTrait.php b/Spanner/src/MutationTrait.php index 2361de3b5836..14c5eaa56f78 100644 --- a/Spanner/src/MutationTrait.php +++ b/Spanner/src/MutationTrait.php @@ -17,9 +17,6 @@ namespace Google\Cloud\Spanner; -use Google\Cloud\Core\ArrayTrait; -use Google\ApiCore\ValidationException; - /** * Common helper methods used for creating array representation of * {@see \Google\Cloud\Spanner\V1\Mutation} @@ -332,6 +329,6 @@ private function flattenKeySet(KeySet $keySet) $keys['keys'] = $this->getValueMapper()->encodeValuesAsSimpleType($keys['keys'], true); } - return array_filter($keys, fn ($v) => !is_null($v));; + return array_filter($keys, fn ($v) => !is_null($v)); } } diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index 95a08ddfe068..a3dce8988a7b 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -17,12 +17,12 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\ArrayTrait; use Google\ApiCore\Serializer; use Google\Cloud\Core\ApiHelperTrait; -use Google\ApiCore\ArrayTrait; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Core\ValidateTrait; -use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\Session\Session; @@ -60,6 +60,7 @@ class Operation use MutationTrait; use TimeTrait; use ValidateTrait; + use FormatKeySetTrait; const OP_INSERT = 'insert'; const OP_UPDATE = 'update'; @@ -949,33 +950,6 @@ private function serializeMutations(array $mutations) return $serializedMutations; } - /** - * @param array $keySet - * @return array Formatted keyset - */ - private function formatKeySet(array $keySet) - { - $keys = $this->pluck('keys', $keySet, false); - if ($keys) { - $keySet['keys'] = array_map( - fn ($key) => $this->formatListForApi((array) $key), - $keys - ); - } - - if (isset($keySet['ranges'])) { - $keySet['ranges'] = array_map(function ($rangeItem) { - return array_map([$this, 'formatListForApi'], $rangeItem); - }, $keySet['ranges']); - - if (empty($keySet['ranges'])) { - unset($keySet['ranges']); - } - } - - return $keySet; - } - /** * Format statements. * @@ -1114,7 +1088,7 @@ private function executeStreamingSql(array $args) private function streamingRead(array $args) { list($data, $callOptions) = $this->splitOptionalArgs($args); - $data['keySet']= $this->formatKeySet($this->pluck('keySet', $data)); + $data['keySet'] = $this->formatKeySet($this->pluck('keySet', $data)); $data['transaction'] = $this->createTransactionSelector($data); $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); $databaseName = $this->pluck('database', $data); diff --git a/Spanner/src/RequestTrait.php b/Spanner/src/RequestTrait.php index 63aa5ba9c86c..9b0f5e0b795a 100644 --- a/Spanner/src/RequestTrait.php +++ b/Spanner/src/RequestTrait.php @@ -17,22 +17,7 @@ namespace Google\Cloud\Spanner; -use Google\Cloud\Spanner\Admin\Database\V1\Backup; -use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupMetadata; -use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupMetadata; -use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseMetadata; -use Google\Cloud\Spanner\Admin\Database\V1\Database; -use Google\Cloud\Spanner\Admin\Database\V1\OptimizeRestoredDatabaseMetadata; -use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseMetadata; -use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlMetadata; -use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigMetadata; -use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceMetadata; -use Google\Cloud\Spanner\Admin\Instance\V1\Instance; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; -use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigMetadata; -use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceMetadata; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Protobuf\GPBEmpty; /** * Shared functionality for Spanner requests. diff --git a/Spanner/src/Session/CacheSessionPool.php b/Spanner/src/Session/CacheSessionPool.php index d70411121d8d..73f953bfaad2 100644 --- a/Spanner/src/Session/CacheSessionPool.php +++ b/Spanner/src/Session/CacheSessionPool.php @@ -127,7 +127,7 @@ class CacheSessionPool implements SessionPoolInterface use SysvTrait; const CACHE_KEY_TEMPLATE = 'cache-session-pool.%s.%s.%s'; - const DURATION_SESSION_LIFETIME = 28*24*3600; // 28 days + const DURATION_SESSION_LIFETIME = 28 * 24 * 3600; // 28 days const DURATION_TWENTY_MINUTES = 1200; const DURATION_ONE_MINUTE = 60; const WINDOW_SIZE = 600; @@ -232,7 +232,7 @@ public function acquire($context = SessionPoolInterface::CONTEXT_READ) { // Try to get a session, run maintenance on the pool, and calculate if // we need to create any new sessions. - list($session, $toCreate) = $this->config['lock']->synchronize(function () { + [$session, $toCreate] = $this->config['lock']->synchronize(function () { $toCreate = []; $session = null; $shouldSave = false; @@ -471,7 +471,7 @@ public function warmup() } $exception = null; - list ($createdSessions, $exception) = $this->createSessions(count($toCreate)); + list($createdSessions, $exception) = $this->createSessions(count($toCreate)); $this->config['lock']->synchronize(function () use ($toCreate, $createdSessions) { $item = $this->cacheItemPool->getItem($this->cacheKey); @@ -1032,7 +1032,7 @@ public function maintain() $maintainInterval = $now - $prevMaintainTime; $maxLifetime = self::SESSION_EXPIRATION_SECONDS - 600; $totalSessionsCount = min($totalSessionsCount, $maintainedSessionsCount); - $meanRefreshCount = (int)($totalSessionsCount * $maintainInterval / $maxLifetime); + $meanRefreshCount = (int) ($totalSessionsCount * $maintainInterval / $maxLifetime); $meanRefreshCount = min($meanRefreshCount, $maintainedSessionsCount); // There may be sessions already refreshed since previous maintenance, // so we can save some refresh requests. diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index 395658a194f3..a59120dc75cc 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -23,9 +23,9 @@ use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\RequestTrait; +use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\DeleteSessionRequest; use Google\Cloud\Spanner\V1\GetSessionRequest; -use Google\Cloud\Spanner\V1\Client\SpannerClient; /** * Represents and manages a single Cloud Spanner session. @@ -46,6 +46,11 @@ class Session */ private $routeToLeader; + /** + * @var string + */ + private $databaseName; + /** * @internal Session is constructed by the {@see Database} class. * diff --git a/Spanner/src/Snapshot.php b/Spanner/src/Snapshot.php index 5b9c004d0e50..ab67f8c97b5c 100644 --- a/Spanner/src/Snapshot.php +++ b/Spanner/src/Snapshot.php @@ -18,7 +18,6 @@ namespace Google\Cloud\Spanner; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Spanner\Session\SessionPoolInterface; /** * Read-only snapshot Transaction. @@ -60,7 +59,7 @@ public function __construct(Operation $operation, Session $session, array $optio { if (isset($options['tag'])) { throw new \InvalidArgumentException( - "Cannot set a transaction tag on a read-only transaction." + 'Cannot set a transaction tag on a read-only transaction.' ); } $this->initialize($operation, $session, $options); diff --git a/Spanner/src/SnapshotTrait.php b/Spanner/src/SnapshotTrait.php index b566cfcb957d..53bbadcd1455 100644 --- a/Spanner/src/SnapshotTrait.php +++ b/Spanner/src/SnapshotTrait.php @@ -20,7 +20,6 @@ use Google\ApiCore\ArrayTrait; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Cloud\Spanner\Timestamp; /** * Common methods for Read-Only transactions (i.e. Snapshots) diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 9bfb449604aa..e56e9d8322e6 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -29,18 +29,16 @@ use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; -use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigOperationsRequest; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsRequest; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Cloud\Spanner\Numeric; -use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; use Google\Protobuf\Duration; use Psr\Cache\CacheItemPoolInterface; @@ -133,6 +131,11 @@ class SpannerClient */ private $projectId; + /** + * @var string + */ + private $projectName; + /** * @var bool */ @@ -937,6 +940,6 @@ public function duration($seconds, $nanos = 0) */ public function commitTimestamp() { - return new CommitTimestamp; + return new CommitTimestamp(); } } diff --git a/Spanner/src/Transaction.php b/Spanner/src/Transaction.php index a6ee6fe2012b..2255aab9f5bd 100644 --- a/Spanner/src/Transaction.php +++ b/Spanner/src/Transaction.php @@ -17,7 +17,6 @@ namespace Google\Cloud\Spanner; -use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Spanner\Session\Session; @@ -112,13 +111,13 @@ public function __construct( if ($this->type == self::TYPE_SINGLE_USE && isset($tag)) { throw new \InvalidArgumentException( - "Cannot set a transaction tag on a single-use transaction." + 'Cannot set a transaction tag on a single-use transaction.' ); } $this->context = SessionPoolInterface::CONTEXT_READWRITE; $this->options = $options; - $this->tag =$tag; + $this->tag = $tag; } /** diff --git a/Spanner/src/TransactionConfigurationTrait.php b/Spanner/src/TransactionConfigurationTrait.php index 88e4ac39f862..aa66f0e51eff 100644 --- a/Spanner/src/TransactionConfigurationTrait.php +++ b/Spanner/src/TransactionConfigurationTrait.php @@ -81,7 +81,7 @@ private function configureDirectedReadOptions(array $requestOptions, array $clie if (isset($requestOptions['transaction']['singleUse']) || ( isset($requestOptions['transactionContext']) && $requestOptions['transactionContext'] == SessionPoolInterface::CONTEXT_READ - ) || isset($requestOptions['transactionOptions']['readOnly']) + ) || isset($requestOptions['transactionOptions']['readOnly']) ) { if (isset($clientOptions['includeReplicas'])) { return ['includeReplicas' => $clientOptions['includeReplicas']]; diff --git a/Spanner/src/V1/SpannerClient.php b/Spanner/src/V1/SpannerClient.php deleted file mode 100644 index 220b1b5ec6a6..000000000000 --- a/Spanner/src/V1/SpannerClient.php +++ /dev/null @@ -1,52 +0,0 @@ -resolveTypeDefinition($type, $key); + list($type, $definition) = $this->resolveTypeDefinition($type, $key); } $paramDefinition = $this->paramType($value, $type, $definition); - list ($parameters[$key], $paramTypes[$key]) = $paramDefinition; + list($parameters[$key], $paramTypes[$key]) = $paramDefinition; } return [ @@ -446,14 +446,14 @@ private function paramType( break; case 'object': - list ($type, $value) = $this->objectParam($value); + list($type, $value) = $this->objectParam($value); break; case 'array': if ($givenType === Database::TYPE_STRUCT) { if (!($definition instanceof StructType)) { throw new \InvalidArgumentException( - 'Struct parameter types must be declared explicitly, and must '. + 'Struct parameter types must be declared explicitly, and must ' . 'be an instance of Google\Cloud\Spanner\StructType.' ); } @@ -462,7 +462,7 @@ private function paramType( $value = (array) $value; } - list ($value, $type) = $this->structParam($value, $definition); + list($value, $type) = $this->structParam($value, $definition); } else { if (!($definition instanceof ArrayType)) { throw new \InvalidArgumentException( @@ -470,7 +470,7 @@ private function paramType( ); } - list ($value, $type) = $this->arrayParam($value, $definition, $allowMixedArrayType); + list($value, $type) = $this->arrayParam($value, $definition, $allowMixedArrayType); } break; @@ -691,7 +691,6 @@ private function arrayParam($value, ArrayType $arrayObj, $allowMixedArrayType = throw new \InvalidArgumentException('Array values may not be of mixed type'); } - // get typeCode either from the array type or the first element's inferred type $typeCode = self::isCustomType($arrayObj->type()) ? self::getTypeCodeFromString($arrayObj->type()) diff --git a/Spanner/tests/Perf/ycsb.php b/Spanner/tests/Perf/ycsb.php index 05749d5bddcd..8d561a067bb7 100644 --- a/Spanner/tests/Perf/ycsb.php +++ b/Spanner/tests/Perf/ycsb.php @@ -56,7 +56,7 @@ $parameters = Config::getParameters(); $report = Report::getReporter(); -$database = (new SpannerClient)->connect($parameters['instance'], $parameters['database']); +$database = (new SpannerClient())->connect($parameters['instance'], $parameters['database']); $totalWeight = 0.0; $weights = []; diff --git a/Spanner/tests/ResultGeneratorTrait.php b/Spanner/tests/ResultGeneratorTrait.php index eeadd1ea3775..a6cd118facc0 100644 --- a/Spanner/tests/ResultGeneratorTrait.php +++ b/Spanner/tests/ResultGeneratorTrait.php @@ -18,6 +18,8 @@ namespace Google\Cloud\Spanner\Tests; use Google\ApiCore\ServerStream; +use Google\Cloud\Spanner\Database; +use Google\Cloud\Spanner\Tests\Unit\Fixtures; use Google\Cloud\Spanner\V1\PartialResultSet; use Google\Cloud\Spanner\V1\ResultSetMetadata; use Google\Cloud\Spanner\V1\ResultSetStats; @@ -25,9 +27,7 @@ use Google\Cloud\Spanner\V1\StructType\Field; use Google\Cloud\Spanner\V1\Transaction; use Google\Cloud\Spanner\V1\Type; -use Google\Cloud\Spanner\Database; use Google\Protobuf\Value; -use Google\Cloud\Spanner\Tests\Unit\Fixtures; /** * Provide a Spanner Read/Query result @@ -107,7 +107,6 @@ private function resultGeneratorStream( $this->stream->readAll() ->willReturn($this->resultGeneratorChunks($chunks)); - } else { $rows = [ [ diff --git a/Spanner/tests/Snippet/ArrayTypeTest.php b/Spanner/tests/Snippet/ArrayTypeTest.php index b576c9cd979d..a5daf6276f32 100644 --- a/Spanner/tests/Snippet/ArrayTypeTest.php +++ b/Spanner/tests/Snippet/ArrayTypeTest.php @@ -17,11 +17,11 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Testing\TestHelpers; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; use Google\Cloud\Spanner\ArrayType; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; diff --git a/Spanner/tests/Snippet/BackupTest.php b/Spanner/tests/Snippet/BackupTest.php index fbd649ff0e0e..570f9939cc46 100644 --- a/Spanner/tests/Snippet/BackupTest.php +++ b/Spanner/tests/Snippet/BackupTest.php @@ -17,12 +17,10 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\LongRunning\Client\OperationsClient; -use Google\Cloud\Core\Iterator\ItemIterator; use Google\ApiCore\OperationResponse; +use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Instance; @@ -30,7 +28,7 @@ use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; - /** +/** * @group spanner * @group spanner-backup */ @@ -60,7 +58,7 @@ public function setUp(): void [['projectId' => 'my-project']], ['requestHandler', 'serializer'] ); - $this->expireTime = new \DateTime("+ 7 hours"); + $this->expireTime = new \DateTime('+ 7 hours'); $this->instance = new Instance( $this->requestHandler->reveal(), $this->serializer, @@ -257,7 +255,6 @@ public function testUpdateExpireTime() $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); $this->backup->___setProperty('serializer', $this->serializer); - $res = $snippet->invoke('info'); $this->assertEquals($bkp, $res->returnVal()); } diff --git a/Spanner/tests/Snippet/Batch/BatchClientTest.php b/Spanner/tests/Snippet/Batch/BatchClientTest.php index d385efca6046..ab84a0c3e08a 100644 --- a/Spanner/tests/Snippet/Batch/BatchClientTest.php +++ b/Spanner/tests/Snippet/Batch/BatchClientTest.php @@ -237,7 +237,7 @@ public function testSnapshot() public function testSnapshotFromString() { - $timestamp = new Timestamp(new \DateTime); + $timestamp = new Timestamp(new \DateTime()); $identifier = base64_encode(json_encode([ 'sessionName' => self::SESSION, diff --git a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php index a50895d1540e..ce9e8fd6b78b 100644 --- a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php @@ -30,7 +30,6 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\V1\Client\SpannerClient; -use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; /** @@ -121,7 +120,7 @@ public function testSerializeSnapshot($index) public function provideSerializeIndex() { - return [[1],[2]]; + return [[1], [2]]; } public function testClose() @@ -163,7 +162,7 @@ public function testPartitionRead($method) public function providePartitionMethods() { - return [['partitionRead'],['partitionQuery']]; + return [['partitionRead'], ['partitionQuery']]; } public function testExecutePartition() diff --git a/Spanner/tests/Snippet/Batch/PartitionSharedSnippetTestTrait.php b/Spanner/tests/Snippet/Batch/PartitionSharedSnippetTestTrait.php index 78e53d5cb95c..6339fd0cc9d0 100644 --- a/Spanner/tests/Snippet/Batch/PartitionSharedSnippetTestTrait.php +++ b/Spanner/tests/Snippet/Batch/PartitionSharedSnippetTestTrait.php @@ -42,7 +42,7 @@ public function testClassSerializeExamples($index) public function provideSerializeSnippetIndex() { - return [[1],[2]]; + return [[1], [2]]; } /** diff --git a/Spanner/tests/Snippet/Batch/QueryPartitionTest.php b/Spanner/tests/Snippet/Batch/QueryPartitionTest.php index 7a4e5528e666..18ddd97e25ab 100644 --- a/Spanner/tests/Snippet/Batch/QueryPartitionTest.php +++ b/Spanner/tests/Snippet/Batch/QueryPartitionTest.php @@ -24,8 +24,6 @@ use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\V1\Client\SpannerClient; -use Prophecy\Argument; /** * @group spanner diff --git a/Spanner/tests/Snippet/Batch/ReadPartitionTest.php b/Spanner/tests/Snippet/Batch/ReadPartitionTest.php index 8cc0efa2de6b..e9b288e84ba2 100644 --- a/Spanner/tests/Snippet/Batch/ReadPartitionTest.php +++ b/Spanner/tests/Snippet/Batch/ReadPartitionTest.php @@ -25,8 +25,6 @@ use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\V1\Client\SpannerClient; -use Prophecy\Argument; /** * @group spanner diff --git a/Spanner/tests/Snippet/BatchDmlResultTest.php b/Spanner/tests/Snippet/BatchDmlResultTest.php index 0a1f7e1e10fe..7ad12585b287 100644 --- a/Spanner/tests/Snippet/BatchDmlResultTest.php +++ b/Spanner/tests/Snippet/BatchDmlResultTest.php @@ -26,7 +26,6 @@ use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; -use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -83,7 +82,7 @@ public function testClass() $this->spannerClient->commit( null, [ - 'commitTimestamp' => $this->formatTimeAsString(new \DateTime, 0) + 'commitTimestamp' => $this->formatTimeAsString(new \DateTime(), 0) ] ); diff --git a/Spanner/tests/Snippet/BytesTest.php b/Spanner/tests/Snippet/BytesTest.php index ea268f6c1554..c75a55efb44f 100644 --- a/Spanner/tests/Snippet/BytesTest.php +++ b/Spanner/tests/Snippet/BytesTest.php @@ -17,10 +17,10 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\Database; -use Google\Cloud\Core\Testing\GrpcTestTrait; use Psr\Http\Message\StreamInterface; /** @@ -63,7 +63,7 @@ public function testGet() $res = $snippet->invoke('stream'); $this->assertInstanceOf(StreamInterface::class, $res->returnVal()); - $this->assertEquals(self::BYTES, (string)$res->returnVal()); + $this->assertEquals(self::BYTES, (string) $res->returnVal()); } public function testType() diff --git a/Spanner/tests/Snippet/CommitTimestampTest.php b/Spanner/tests/Snippet/CommitTimestampTest.php index c8cd4abb0d68..ac1d16c48fe9 100644 --- a/Spanner/tests/Snippet/CommitTimestampTest.php +++ b/Spanner/tests/Snippet/CommitTimestampTest.php @@ -19,12 +19,10 @@ use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\Timestamp; use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\SpannerClient; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; -use Prophecy\Argument; /** * @group spanner diff --git a/Spanner/tests/Snippet/DatabaseTest.php b/Spanner/tests/Snippet/DatabaseTest.php index 29e80bd43d07..c53f687c92d6 100644 --- a/Spanner/tests/Snippet/DatabaseTest.php +++ b/Spanner/tests/Snippet/DatabaseTest.php @@ -17,10 +17,9 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\LongRunning\Client\OperationsClient; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; @@ -87,7 +86,8 @@ public function setUp(): void $this->requestHandler->reveal(), $this->serializer, self::PROJECT, - self::INSTANCE ); + self::INSTANCE + ); $this->database = TestHelpers::stub(Database::class, [ $this->requestHandler->reveal(), @@ -164,7 +164,7 @@ public function testBackups() ); $this->instance->___setProperty('requestHandler', $this->requestHandler->reveal()); - $res = $snippet->invoke('backups'); + $res = $snippet->invoke('backups'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertContainsOnlyInstancesOf(Backup::class, $res->returnVal()); @@ -430,7 +430,7 @@ public function testSnapshotReadTimestamp() null, [ 'id' => self::TRANSACTION, - 'readTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'readTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -452,7 +452,7 @@ public function testRunTransaction() $this->spannerClient->commit( null, - ['commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString()] + ['commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString()] ); $this->spannerClient->executeStreamingSql( @@ -548,7 +548,7 @@ function ($args) { return isset($message['mutations'][0]['insert']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -559,7 +559,6 @@ function ($args) { $res = $snippet->invoke(); } - public function testInsertBatch() { $this->spannerClient->commit( @@ -569,7 +568,7 @@ function ($args) { && isset($message['mutations'][1]['insert']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -588,7 +587,7 @@ function ($args) { return isset($message['mutations'][0]['update']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -599,7 +598,6 @@ function ($args) { $res = $snippet->invoke(); } - public function testUpdateBatch() { $this->spannerClient->commit( @@ -609,7 +607,7 @@ function ($args) { && isset($message['mutations'][1]['update']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -628,7 +626,7 @@ function ($args) { return isset($message['mutations'][0]['insertOrUpdate']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -639,7 +637,6 @@ function ($args) { $res = $snippet->invoke(); } - public function testInsertOrUpdateBatch() { $this->spannerClient->commit( @@ -649,7 +646,7 @@ function ($args) { && isset($message['mutations'][1]['insertOrUpdate']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -668,7 +665,7 @@ function ($args) { return isset($message['mutations'][0]['replace']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -679,7 +676,6 @@ function ($args) { $res = $snippet->invoke(); } - public function testReplaceBatch() { $this->spannerClient->commit( @@ -689,7 +685,7 @@ function ($args) { && isset($message['mutations'][1]['replace']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -708,7 +704,7 @@ function ($args) { return isset($message['mutations'][0]['delete']); }, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); diff --git a/Spanner/tests/Snippet/DateTest.php b/Spanner/tests/Snippet/DateTest.php index d73aa7dc9ab0..54971ee2b429 100644 --- a/Spanner/tests/Snippet/DateTest.php +++ b/Spanner/tests/Snippet/DateTest.php @@ -17,10 +17,10 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Date; -use Google\Cloud\Core\Testing\GrpcTestTrait; /** * @group spanner @@ -36,7 +36,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->dt = new \DateTimeImmutable; + $this->dt = new \DateTimeImmutable(); $this->date = new Date($this->dt); } diff --git a/Spanner/tests/Snippet/InstanceConfigurationTest.php b/Spanner/tests/Snippet/InstanceConfigurationTest.php index 1e804e5af775..bc73f1a004b1 100644 --- a/Spanner/tests/Snippet/InstanceConfigurationTest.php +++ b/Spanner/tests/Snippet/InstanceConfigurationTest.php @@ -47,12 +47,13 @@ public function setUp(): void $this->checkAndSkipGrpcTests(); $this->serializer = new Serializer(); - $this->config = TestHelpers::stub(InstanceConfiguration::class, [ + $this->config = new InstanceConfiguration( $this->requestHandler->reveal(), $this->serializer, self::PROJECT, self::CONFIG, - [], ); + [], + ); } public function testClass() diff --git a/Spanner/tests/Snippet/InstanceTest.php b/Spanner/tests/Snippet/InstanceTest.php index 034711596c9c..b0ac6e52b56d 100644 --- a/Spanner/tests/Snippet/InstanceTest.php +++ b/Spanner/tests/Snippet/InstanceTest.php @@ -17,13 +17,11 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\LongRunning\Client\OperationsClient; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Backup; @@ -100,7 +98,7 @@ public function testCreate() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('operation'); + $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } @@ -129,7 +127,7 @@ public function testInfo() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke(); + $res = $snippet->invoke(); $this->assertEquals('1', $res->output()); } @@ -149,7 +147,7 @@ public function testExists() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke(); + $res = $snippet->invoke(); $this->assertEquals('Instance exists!', $res->output()); } @@ -169,7 +167,7 @@ public function testReload() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('info'); + $res = $snippet->invoke('info'); $info = $this->instance->info(); $this->assertEquals($info, $res->returnVal()); } @@ -191,7 +189,7 @@ public function testState() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke(); + $res = $snippet->invoke(); $this->assertEquals('Instance is ready!', $res->output()); } @@ -211,7 +209,7 @@ public function testUpdate() 'requestHandler', $this->requestHandler->reveal() ); - $snippet->invoke(); + $snippet->invoke(); } public function testDelete() @@ -225,7 +223,7 @@ public function testDelete() 'requestHandler', $this->requestHandler->reveal() ); - $snippet->invoke(); + $snippet->invoke(); } public function testCreateDatabase() @@ -244,7 +242,7 @@ public function testCreateDatabase() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('operation'); + $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } @@ -266,7 +264,7 @@ public function testCreateDatabaseFromBackup() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('operation'); + $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } @@ -306,7 +304,7 @@ public function testDatabases() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('databases'); + $res = $snippet->invoke('databases'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(Database::class, $res->returnVal()->current()); @@ -348,7 +346,7 @@ public function testBackups() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('backups'); + $res = $snippet->invoke('backups'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(Backup::class, $res->returnVal()->current()); @@ -357,7 +355,7 @@ public function testBackups() public function testBackupOperations() { $backupOperationName = sprintf( - "%s/operations/%s", + '%s/operations/%s', DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP), self::OPERATION ); @@ -382,7 +380,7 @@ public function testBackupOperations() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('backupOperations'); + $res = $snippet->invoke('backupOperations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()->current()); @@ -391,7 +389,7 @@ public function testBackupOperations() public function testDatabaseOperations() { $databaseOperationName = sprintf( - "%s/operations/%s", + '%s/operations/%s', DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE), self::OPERATION ); @@ -416,7 +414,7 @@ public function testDatabaseOperations() 'requestHandler', $this->requestHandler->reveal() ); - $res = $snippet->invoke('databaseOperations'); + $res = $snippet->invoke('databaseOperations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()->current()); @@ -459,7 +457,7 @@ public function testLongRunningOperations() ->willReturn([$this->getOperationResponseMock()]); $this->instance->___setProperty('requestHandler', $this->requestHandler->reveal()); - $res = $snippet->invoke('operations'); + $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertContainsOnlyInstancesOf(OperationResponse::class, $res->returnVal()); } diff --git a/Spanner/tests/Snippet/KeyRangeTest.php b/Spanner/tests/Snippet/KeyRangeTest.php index 6bfe45eb6c7c..905408e022d8 100644 --- a/Spanner/tests/Snippet/KeyRangeTest.php +++ b/Spanner/tests/Snippet/KeyRangeTest.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\KeyRange; -use Google\Cloud\Core\Testing\GrpcTestTrait; /** * @group spanner @@ -34,7 +34,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->range = new KeyRange; + $this->range = new KeyRange(); } public function testClass() diff --git a/Spanner/tests/Snippet/KeySetTest.php b/Spanner/tests/Snippet/KeySetTest.php index 6134a7f730a9..784d9a948211 100644 --- a/Spanner/tests/Snippet/KeySetTest.php +++ b/Spanner/tests/Snippet/KeySetTest.php @@ -17,10 +17,10 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Core\Testing\GrpcTestTrait; /** * @group spanner diff --git a/Spanner/tests/Snippet/NumericTest.php b/Spanner/tests/Snippet/NumericTest.php index f7e4fe753cc9..921d92143080 100644 --- a/Spanner/tests/Snippet/NumericTest.php +++ b/Spanner/tests/Snippet/NumericTest.php @@ -19,7 +19,6 @@ use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Numeric; -use Google\Cloud\Core\Testing\GrpcTestTrait; /** * @group spanner diff --git a/Spanner/tests/Snippet/ResultTest.php b/Spanner/tests/Snippet/ResultTest.php index c00cf77f3d2e..4644241572cd 100644 --- a/Spanner/tests/Snippet/ResultTest.php +++ b/Spanner/tests/Snippet/ResultTest.php @@ -17,13 +17,13 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Snapshot; use Google\Cloud\Spanner\Transaction; -use Google\Cloud\Core\Testing\GrpcTestTrait; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; diff --git a/Spanner/tests/Snippet/Session/CacheSessionPoolTest.php b/Spanner/tests/Snippet/Session/CacheSessionPoolTest.php index e54b6e6e646b..140880cc0373 100644 --- a/Spanner/tests/Snippet/Session/CacheSessionPoolTest.php +++ b/Spanner/tests/Snippet/Session/CacheSessionPoolTest.php @@ -35,7 +35,7 @@ public function testClass() $snippet = $this->snippetFromClass(CacheSessionPool::class); $snippet->replace('$cache =', '//$cache ='); - $snippet->addLocal('cache', new MemoryCacheItemPool); + $snippet->addLocal('cache', new MemoryCacheItemPool()); $res = $snippet->invoke('database'); $this->assertInstanceOf(Database::class, $res->returnVal()); } @@ -48,7 +48,7 @@ public function testClassLabels() $snippet = $this->snippetFromClass(CacheSessionPool::class, 1); $snippet->replace('$cache =', '//$cache ='); - $snippet->addLocal('cache', new MemoryCacheItemPool); + $snippet->addLocal('cache', new MemoryCacheItemPool()); $res = $snippet->invoke(); } @@ -60,7 +60,7 @@ public function testClassWithDatabaseRole() $snippet = $this->snippetFromClass(CacheSessionPool::class, 2); $snippet->replace('$cache =', '//$cache ='); - $snippet->addLocal('cache', new MemoryCacheItemPool); + $snippet->addLocal('cache', new MemoryCacheItemPool()); $res = $snippet->invoke('database'); $this->assertInstanceOf(Database::class, $res->returnVal()); } diff --git a/Spanner/tests/Snippet/SnapshotTest.php b/Spanner/tests/Snippet/SnapshotTest.php index 9fd174da2b66..5dd38ddea676 100644 --- a/Spanner/tests/Snippet/SnapshotTest.php +++ b/Spanner/tests/Snippet/SnapshotTest.php @@ -54,7 +54,7 @@ public function setUp(): void $session->reveal(), [ 'id' => self::TRANSACTION, - 'readTimestamp' => new Timestamp(new \DateTime) + 'readTimestamp' => new Timestamp(new \DateTime()) ] ], ['operation']); } diff --git a/Spanner/tests/Snippet/SpannerClientTest.php b/Spanner/tests/Snippet/SpannerClientTest.php index 3f9a629f1d9a..d67616e81bc6 100644 --- a/Spanner/tests/Snippet/SpannerClientTest.php +++ b/Spanner/tests/Snippet/SpannerClientTest.php @@ -17,12 +17,11 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Bytes; @@ -33,14 +32,13 @@ use Google\Cloud\Spanner\InstanceConfiguration; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Spanner\SpannerClient; -use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Numeric; +use Google\Cloud\Spanner\PgJsonb; use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\PgOid; -use Google\Cloud\Spanner\PgJsonb; +use Google\Cloud\Spanner\SpannerClient; +use Google\Cloud\Spanner\Timestamp; use Google\Protobuf\Duration; -use Prophecy\Argument; /** * @group spanner @@ -100,7 +98,6 @@ public function testInstanceConfigurations() ] ); - $snippet = $this->snippetFromMethod(SpannerClient::class, 'instanceConfigurations'); $snippet->addLocal('spanner', $this->client); @@ -146,7 +143,6 @@ public function testCreateInstance() $this->getOperationResponseMock() ); - $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } @@ -187,7 +183,6 @@ public function testInstances() ] ); - $res = $snippet->invoke('instances'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); $this->assertInstanceOf(Instance::class, $res->returnVal()->current()); diff --git a/Spanner/tests/Snippet/StructTypeTest.php b/Spanner/tests/Snippet/StructTypeTest.php index d0d5c5427ceb..2b0391935dfe 100644 --- a/Spanner/tests/Snippet/StructTypeTest.php +++ b/Spanner/tests/Snippet/StructTypeTest.php @@ -27,7 +27,6 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructType; -use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -83,7 +82,7 @@ public function setUp(): void $sessionPool->reveal() ], ['operation', 'requestHandler', 'serializer']); - $this->type = new StructType; + $this->type = new StructType(); } public function testExecuteStruct() @@ -93,15 +92,15 @@ public function testExecuteStruct() 'name' => 'firstName', 'type' => [ 'code' => Database::TYPE_STRING, - "typeAnnotation" => 0, - "protoTypeFqn" => "" + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ 'name' => 'lastName', 'type' => [ 'code' => Database::TYPE_STRING, - "typeAnnotation" => 0, - "protoTypeFqn" => "" + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ] ]; @@ -172,7 +171,7 @@ public function testAddComplex() [ 'name' => 'customer', 'type' => Database::TYPE_STRUCT, - 'child' => (new StructType) + 'child' => (new StructType()) ->add('name', Database::TYPE_STRING) ->add('phone', Database::TYPE_STRING) ->add('email', Database::TYPE_STRING) diff --git a/Spanner/tests/Snippet/StructValueTest.php b/Spanner/tests/Snippet/StructValueTest.php index 8eecb4e434c0..24601f3c8e86 100644 --- a/Spanner/tests/Snippet/StructValueTest.php +++ b/Spanner/tests/Snippet/StructValueTest.php @@ -26,7 +26,6 @@ use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructValue; -use Google\Cloud\Spanner\V1\Client\SpannerClient; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -82,7 +81,7 @@ public function setUp(): void $sessionPool->reveal() ], ['operation']); - $this->value = new StructValue; + $this->value = new StructValue(); } public function testConstructor() @@ -103,7 +102,7 @@ public function testConstructor() 'protoTypeFqn' => '' ] ], [ - 'name'=> '', + 'name' => '', 'type' => [ 'code' => Database::TYPE_STRING, 'typeAnnotation' => 0, diff --git a/Spanner/tests/Snippet/TimestampTest.php b/Spanner/tests/Snippet/TimestampTest.php index 24be6f0a7256..f29869bf2b77 100644 --- a/Spanner/tests/Snippet/TimestampTest.php +++ b/Spanner/tests/Snippet/TimestampTest.php @@ -17,10 +17,10 @@ namespace Google\Cloud\Spanner\Tests\Snippet; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Core\Testing\GrpcTestTrait; /** * @group spanner @@ -35,7 +35,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->dt = new \DateTime; + $this->dt = new \DateTime(); $this->timestamp = new Timestamp($this->dt); } @@ -52,7 +52,7 @@ public function testClassCast() $snippet->addLocal('timestamp', $this->timestamp); $res = $snippet->invoke(); - $this->assertEquals((string)$this->timestamp, $res->output()); + $this->assertEquals((string) $this->timestamp, $res->output()); } public function testGet() diff --git a/Spanner/tests/Snippet/TransactionTest.php b/Spanner/tests/Snippet/TransactionTest.php index 38f03431c947..7b8c7cff2fd2 100644 --- a/Spanner/tests/Snippet/TransactionTest.php +++ b/Spanner/tests/Snippet/TransactionTest.php @@ -30,8 +30,8 @@ use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; -use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -131,26 +131,26 @@ public function testExecuteUpdate() public function testExecuteUpdateWithStruct() { $expectedSql = "UPDATE Posts SET title = 'Updated Title' WHERE " . - "STRUCT(Title, Content) = @post"; + 'STRUCT<Title STRING, Content STRING>(Title, Content) = @post'; $expectedParams = [ - 'post' => ["Updated Title", "Sample Content"] + 'post' => ['Updated Title', 'Sample Content'] ]; $expectedStructData = [ [ - "name" => "Title", - "type" => [ - "code" => Database::TYPE_STRING, - "typeAnnotation" => 0, - "protoTypeFqn" => "" + 'name' => 'Title', + 'type' => [ + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ], [ - "name" => "Content", - "type" => [ - "code" => Database::TYPE_STRING, - "typeAnnotation" => 0, - "protoTypeFqn" => "" + 'name' => 'Content', + 'type' => [ + 'code' => Database::TYPE_STRING, + 'typeAnnotation' => 0, + 'protoTypeFqn' => '' ] ] ]; @@ -279,7 +279,6 @@ public function testInsert() $this->assertArrayHasKey('insert', $mutations[0]); } - public function testInsertBatch() { $snippet = $this->snippetFromMethod(Transaction::class, 'insertBatch'); @@ -314,7 +313,6 @@ public function testUpdate() $this->assertArrayHasKey('update', $mutations[0]); } - public function testUpdateBatch() { $snippet = $this->snippetFromMethod(Transaction::class, 'updateBatch'); @@ -349,7 +347,6 @@ public function testInsertOrUpdate() $this->assertArrayHasKey('insertOrUpdate', $mutations[0]); } - public function testInsertOrUpdateBatch() { $this->refreshOperation( @@ -390,7 +387,6 @@ public function testReplace() $this->assertArrayHasKey('replace', $mutations[0]); } - public function testReplaceBatch() { $snippet = $this->snippetFromMethod(Transaction::class, 'replaceBatch'); @@ -447,7 +443,7 @@ public function testCommit() $this->spannerClient->commit( null, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString() + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString() ] ); @@ -469,7 +465,7 @@ public function testGetCommitStats() $this->spannerClient->commit( null, [ - 'commitTimestamp' => (new Timestamp(new \DateTime))->formatAsString(), + 'commitTimestamp' => (new Timestamp(new \DateTime()))->formatAsString(), 'commitStats' => $expectedCommitStats, ] ); diff --git a/Spanner/tests/Snippet/TransactionalReadMethodsTest.php b/Spanner/tests/Snippet/TransactionalReadMethodsTest.php index b0e0fe5f4e98..9943f3c7a613 100644 --- a/Spanner/tests/Snippet/TransactionalReadMethodsTest.php +++ b/Spanner/tests/Snippet/TransactionalReadMethodsTest.php @@ -491,7 +491,7 @@ private function setupSnapshot() $this->session->reveal(), [ 'id' => self::TRANSACTION, - 'readTimestamp' => new Timestamp(new \DateTime) + 'readTimestamp' => new Timestamp(new \DateTime()) ] ], ['operation']); } diff --git a/Spanner/tests/System/AdminTest.php b/Spanner/tests/System/AdminTest.php index 75af77638313..b0808a53ca1f 100644 --- a/Spanner/tests/System/AdminTest.php +++ b/Spanner/tests/System/AdminTest.php @@ -17,13 +17,11 @@ namespace Google\Cloud\Spanner\Tests\System; -use Google\Cloud\Core\Exception\FailedPreconditionException; -use Google\Cloud\Core\LongRunning\LongRunningOperation; use Google\ApiCore\OperationResponse; +use Google\Cloud\Core\Exception\FailedPreconditionException; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig\Type; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; @@ -115,7 +113,7 @@ public function testDatabase() $expectedDatabaseDialect = DatabaseDialect::GOOGLE_STANDARD_SQL; // TODO: Remove this, when the emulator supports PGSQL - if ((bool) getenv("SPANNER_EMULATOR_HOST")) { + if ((bool) getenv('SPANNER_EMULATOR_HOST')) { $expectedDatabaseDialect = DatabaseDialect::DATABASE_DIALECT_UNSPECIFIED; } @@ -123,7 +121,7 @@ public function testDatabase() $stmt = "CREATE TABLE Ids (\n" . " id INT64 NOT NULL,\n" . - ") PRIMARY KEY(id)"; + ') PRIMARY KEY(id)'; $op = $db->updateDdl($stmt); $op->pollUntilComplete(); diff --git a/Spanner/tests/System/BackupTest.php b/Spanner/tests/System/BackupTest.php index 575093f24bc3..3d4ceae147e6 100644 --- a/Spanner/tests/System/BackupTest.php +++ b/Spanner/tests/System/BackupTest.php @@ -17,14 +17,13 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Exception\BadRequestException; use Google\Cloud\Core\Exception\ConflictException; -use Google\ApiCore\OperationResponse; use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupEncryptionConfig; -use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Database\V1\EncryptionConfig; use Google\Cloud\Spanner\Admin\Database\V1\EncryptionInfo\Type; +use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Date; @@ -77,7 +76,7 @@ public static function setUpTestFixtures(): void }); $db1->updateDdl( - 'CREATE TABLE '. self::TEST_TABLE_NAME .' ( + 'CREATE TABLE ' . self::TEST_TABLE_NAME . ' ( id INT64 NOT NULL, name STRING(MAX) NOT NULL, birthday DATE NOT NULL @@ -95,7 +94,7 @@ public static function setUpTestFixtures(): void }); $db2->updateDdl( - 'CREATE TABLE '. self::TEST_TABLE_NAME .' ( + 'CREATE TABLE ' . self::TEST_TABLE_NAME . ' ( id INT64 NOT NULL, name STRING(MAX) NOT NULL, birthday DATE NOT NULL @@ -106,8 +105,8 @@ public static function setUpTestFixtures(): void self::insertData(10, self::$dbName2); self::$backupId1 = uniqid(self::BACKUP_PREFIX); - self::$backupId2 = uniqid("users-"); - self::$copyBackupId = uniqid("copy-"); + self::$backupId2 = uniqid('users-'); + self::$copyBackupId = uniqid('copy-'); self::$hasSetUp = true; } @@ -198,7 +197,7 @@ public function testCreateBackupInvalidArgument() $e = null; try { $backup->create(self::$dbName1, $expireTime, [ - 'versionTime' => "invalidType", + 'versionTime' => 'invalidType', ]); } catch (\InvalidArgumentException $e) { } @@ -366,7 +365,7 @@ public function testListAllBackupsContainsName() */ public function testListAllBackupsReady() { - $backups = iterator_to_array(self::$instance->backups(['filter'=>'state:READY'])); + $backups = iterator_to_array(self::$instance->backups(['filter' => 'state:READY'])); $backupNames = []; foreach ($backups as $b) { @@ -396,9 +395,9 @@ public function testListAllBackupsOfDatabase() */ public function testListAllBackupsCreatedAfterTimestamp() { - $filter = sprintf("create_time >= %s", self::$createTime1); + $filter = sprintf('create_time >= %s', self::$createTime1); - $backups = iterator_to_array(self::$instance->backups(['filter'=>$filter])); + $backups = iterator_to_array(self::$instance->backups(['filter' => $filter])); $backupNames = []; foreach ($backups as $b) { @@ -413,9 +412,9 @@ public function testListAllBackupsCreatedAfterTimestamp() */ public function testListAllBackupsExpireBeforeTimestamp() { - $filter = "expire_time < " . gmdate('"Y-m-d\TH:i:s\Z"', strtotime('+9 hours')); + $filter = 'expire_time < ' . gmdate('"Y-m-d\TH:i:s\Z"', strtotime('+9 hours')); - $backups = iterator_to_array(self::$instance->backups(['filter'=>$filter])); + $backups = iterator_to_array(self::$instance->backups(['filter' => $filter])); $backupNames = []; foreach ($backups as $b) { @@ -433,7 +432,7 @@ public function testListAllBackupsWithSizeGreaterThanSomeBytes() { $backup = self::$instance->backup(self::$backupId1); $size = $backup->info()['sizeBytes']; - $filter = "size_bytes > " . $size; + $filter = 'size_bytes > ' . $size; $backups = iterator_to_array(self::$instance->backups(['filter' => $filter])); @@ -501,7 +500,7 @@ public function testDeleteBackup() public function testDeleteNonExistantBackup() { - $backup = self::$instance->backup("does_not_exis"); + $backup = self::$instance->backup('does_not_exis'); $this->assertFalse($backup->exists()); @@ -633,7 +632,7 @@ private static function generateRows($number) { $rows = []; - for ($id=1; $id <= $number; $id++) { + for ($id = 1; $id <= $number; $id++) { $rows[] = self::generateRow($id, uniqid(self::TESTING_PREFIX), new Date(new \DateTime())); } return $rows; diff --git a/Spanner/tests/System/BatchTest.php b/Spanner/tests/System/BatchTest.php index 896d21bd6d85..759518d9cbcb 100644 --- a/Spanner/tests/System/BatchTest.php +++ b/Spanner/tests/System/BatchTest.php @@ -17,12 +17,12 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Cloud\Core\Exception\ServiceException; +use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\BatchSnapshot; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Core\Exception\ServiceException; /** * @group spanner @@ -84,7 +84,7 @@ public static function setUpTestFixtures(): void private static function seedTable() { - $decades = [1950,1960,1970,1980,1990,2000]; + $decades = [1950, 1960, 1970, 1980, 1990, 2000]; for ($i = 0; $i < 250; $i++) { self::$database->insert(self::$tableName, [ 'id' => self::randId(), diff --git a/Spanner/tests/System/BatchWriteTest.php b/Spanner/tests/System/BatchWriteTest.php index cbcf38031985..d4ce3869f8f8 100644 --- a/Spanner/tests/System/BatchWriteTest.php +++ b/Spanner/tests/System/BatchWriteTest.php @@ -53,16 +53,16 @@ public function testBatchWrite() $mutationGroups = []; $mutationGroups[] = self::$database->mutationGroup() ->insertOrUpdate( - "Singers", + 'Singers', ['SingerId' => 16, 'FirstName' => 'Scarlet', 'LastName' => 'Terry'] ); $mutationGroups[] = self::$database->mutationGroup() ->insertOrUpdate( - "Singers", + 'Singers', ['SingerId' => 17, 'FirstName' => 'Marc', 'LastName' => 'Kristen'] )->insertOrUpdate( - "Albums", + 'Albums', ['AlbumId' => 1, 'SingerId' => 17, 'AlbumTitle' => 'Total Junk'] ); diff --git a/Spanner/tests/System/GeneratedAdminEmulatorTest.php b/Spanner/tests/System/GeneratedAdminEmulatorTest.php index b31d5f409ac0..cef39e7faef7 100644 --- a/Spanner/tests/System/GeneratedAdminEmulatorTest.php +++ b/Spanner/tests/System/GeneratedAdminEmulatorTest.php @@ -42,7 +42,7 @@ public static function setUpTestFixtures(): void public function testAdminClientEmulatorSupport() { - if (!getenv("SPANNER_EMULATOR_HOST")) { + if (!getenv('SPANNER_EMULATOR_HOST')) { self::markTestSkipped('This test is required to run only in the emulator.'); } diff --git a/Spanner/tests/System/LargeReadTest.php b/Spanner/tests/System/LargeReadTest.php index e491aca6e8f4..4fe2217c22ac 100644 --- a/Spanner/tests/System/LargeReadTest.php +++ b/Spanner/tests/System/LargeReadTest.php @@ -31,7 +31,7 @@ class LargeReadTest extends SpannerTestCase //@codingStandardsIgnoreStart private static $data = [ - 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ]; //@codingStandardsIgnoreEnd @@ -80,7 +80,7 @@ private static function seedTable() 'bytesArrayColumn' => self::randomArrayOfBytes($str), ]; - for ($i=0; $i < 10; $i++) { + for ($i = 0; $i < 10; $i++) { self::$database->insert(self::$tableName, self::$row + ['id' => self::randId()], [ 'timeoutMillis' => 50000 ]); @@ -143,7 +143,7 @@ private static function randomBytes(&$str) private static function randomArrayOfStrings() { $res = []; - for ($i=0; $i <= rand(1, 4); $i++) { + for ($i = 0; $i <= rand(1, 4); $i++) { $res[] = self::randomString(); } @@ -153,7 +153,7 @@ private static function randomArrayOfStrings() private static function randomArrayOfBytes(&$str) { $res = []; - for ($i=0; $i <= rand(1, 4); $i++) { + for ($i = 0; $i <= rand(1, 4); $i++) { $res[] = self::randomBytes($str); } diff --git a/Spanner/tests/System/OperationsTest.php b/Spanner/tests/System/OperationsTest.php index 50e88cb6fb80..77b857206403 100644 --- a/Spanner/tests/System/OperationsTest.php +++ b/Spanner/tests/System/OperationsTest.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Spanner\Date; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Core\Exception\ServiceException; /** * @group spanner @@ -147,7 +147,7 @@ public function testEmptyRead() $keySet = self::$client->keySet(['keys' => [99999]]); - $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id','name']); + $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id', 'name']); $this->assertEmpty(iterator_to_array($res->rows())); } @@ -157,7 +157,7 @@ public function testEmptyReadOnIndex() $keySet = self::$client->keySet(['keys' => [99999]]); - $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id','name'], [ + $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id', 'name'], [ 'index' => self::TEST_INDEX_NAME ]); @@ -185,7 +185,7 @@ public function testReadNonExistentSingleKey() 'keys' => [99999] ]); - $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id','name']); + $res = $db->read(self::TEST_TABLE_NAME, $keySet, ['id', 'name']); $this->assertEmpty(iterator_to_array($res->rows())); } diff --git a/Spanner/tests/System/PartitionedDmlTest.php b/Spanner/tests/System/PartitionedDmlTest.php index 50f4ed7ded02..a2973b968ef9 100644 --- a/Spanner/tests/System/PartitionedDmlTest.php +++ b/Spanner/tests/System/PartitionedDmlTest.php @@ -17,8 +17,6 @@ namespace Google\Cloud\Spanner\Tests\System; -use Google\Cloud\Spanner\Tests\System\SpannerTestCase; - /** * @group spanner * @group spanner-pdml diff --git a/Spanner/tests/System/PgBatchTest.php b/Spanner/tests/System/PgBatchTest.php index 131ba743a084..de4dca0b0267 100644 --- a/Spanner/tests/System/PgBatchTest.php +++ b/Spanner/tests/System/PgBatchTest.php @@ -17,10 +17,10 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Cloud\Core\Exception\ServiceException; +use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\BatchSnapshot; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; -use Google\Cloud\Core\Exception\ServiceException; /** * @group spanner @@ -140,7 +140,7 @@ private function executePartitions(BatchClient $client, BatchSnapshot $snapshot, private static function seedTable() { - $decades = [1950,1960,1970,1980,1990,2000]; + $decades = [1950, 1960, 1970, 1980, 1990, 2000]; for ($i = 0; $i < 250; $i++) { self::$database->insert(self::$tableName, [ 'id' => self::randId(), diff --git a/Spanner/tests/System/PgBatchWriteTest.php b/Spanner/tests/System/PgBatchWriteTest.php index d7563bbe5ccf..dc09cfdc8834 100644 --- a/Spanner/tests/System/PgBatchWriteTest.php +++ b/Spanner/tests/System/PgBatchWriteTest.php @@ -55,16 +55,16 @@ public function testBatchWrite() $mutationGroups = []; $mutationGroups[] = self::$database->mutationGroup() ->insertOrUpdate( - "Singers", + 'Singers', ['SingerId' => 16, 'FirstName' => 'Scarlet', 'LastName' => 'Terry'] ); $mutationGroups[] = self::$database->mutationGroup() ->insertOrUpdate( - "Singers", + 'Singers', ['SingerId' => 17, 'FirstName' => 'Marc', 'LastName' => 'Kristen'] )->insertOrUpdate( - "Albums", + 'Albums', ['AlbumId' => 1, 'SingerId' => 17, 'AlbumTitle' => 'Total Junk'] ); diff --git a/Spanner/tests/System/PgPartitionedDmlTest.php b/Spanner/tests/System/PgPartitionedDmlTest.php index 6906090f51bd..e9fc42e0a591 100644 --- a/Spanner/tests/System/PgPartitionedDmlTest.php +++ b/Spanner/tests/System/PgPartitionedDmlTest.php @@ -17,8 +17,6 @@ namespace Google\Cloud\Spanner\Tests\System; -use Google\Cloud\Spanner\Tests\System\SpannerPgTestCase; - /** * @group spanner * @group spanner-pdml diff --git a/Spanner/tests/System/PgQueryTest.php b/Spanner/tests/System/PgQueryTest.php index b45766cfa491..38ddefea403a 100644 --- a/Spanner/tests/System/PgQueryTest.php +++ b/Spanner/tests/System/PgQueryTest.php @@ -23,8 +23,8 @@ use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Date; -use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\PgJsonb; +use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\V1\RequestOptions\Priority; @@ -63,7 +63,7 @@ public static function setUpTestFixtures(): void )' )->pollUntilComplete(); - self::$timestampVal = new Timestamp(new \DateTime); + self::$timestampVal = new Timestamp(new \DateTime()); self::$database->insertOrUpdateBatch(self::TABLE_NAME, [ [ @@ -312,7 +312,7 @@ public function testBindPgNumericParameter() $row = $res->rows()->current(); $this->assertInstanceOf(PgNumeric::class, $row['age']); $this->assertEquals($str, $val->formatAsString()); - $this->assertEquals($str, (string)$val->get()); + $this->assertEquals($str, (string) $val->get()); } public function testBindPgNumericParameterNull() @@ -358,7 +358,7 @@ public function testBindBytesParameter() $row = $res->rows()->current(); $this->assertInstanceOf(Bytes::class, $row['bytes_col']); $this->assertEquals($str, base64_decode($bytes->formatAsString())); - $this->assertEquals($str, (string)$bytes->get()); + $this->assertEquals($str, (string) $bytes->get()); } public function testBindBytesParameterNull() @@ -437,7 +437,7 @@ public function testBindTimestampParameterNull() public function testBindDateParameter() { - $res = self::$database->execute("SELECT * FROM " . self::TABLE_NAME . " WHERE dt BETWEEN $1 AND $2", [ + $res = self::$database->execute('SELECT * FROM ' . self::TABLE_NAME . ' WHERE dt BETWEEN $1 AND $2', [ 'parameters' => [ 'p1' => new Date(new \DateTime('2020-01-01')), 'p2' => new Date(new \DateTime('2021-01-01')) @@ -512,7 +512,7 @@ public function testBindPgJsonbParameter() $row = $res->rows()->current(); $this->assertInstanceOf(PgJsonb::class, $row['data']); $this->assertEquals($str, $val->formatAsString()); - $this->assertEquals($str, (string)$val->get()); + $this->assertEquals($str, (string) $val->get()); } public function testBindJsonbParameterNull() @@ -579,16 +579,16 @@ public function arrayTypesProvider() { return [ // boolean - [[true,true,false]], + [[true, true, false]], // int64 - [[5,4,3,2,1]], + [[5, 4, 3, 2, 1]], // float64 [[3.14, 4.13, 1.43]], // string - [['hello','world','google','cloud']], + [['hello', 'world', 'google', 'cloud']], // bytes [ @@ -661,7 +661,7 @@ function (array $res) { [ new PgJsonb('{}'), new PgJsonb('{"a": "b"}'), - new PgJsonb(["a" => "b"]) + new PgJsonb(['a' => 'b']) ], ['{}', '{"a": "b"}', '{"a": "b"}'], PgJsonb::class, @@ -674,7 +674,7 @@ function (array $res) { } ], // pg_oid - [[5,4,3,2,1]], + [[5, 4, 3, 2, 1]], ]; } diff --git a/Spanner/tests/System/PgReadTest.php b/Spanner/tests/System/PgReadTest.php index af14f9910966..1737c95710de 100644 --- a/Spanner/tests/System/PgReadTest.php +++ b/Spanner/tests/System/PgReadTest.php @@ -41,8 +41,8 @@ public static function setUpTestFixtures(): void { parent::setUpTestFixtures(); - self::$readTableName = "read_table"; - self::$rangeTableName = "range_table"; + self::$readTableName = 'read_table'; + self::$rangeTableName = 'range_table'; $create = 'CREATE TABLE %s ( id bigint NOT NULL, diff --git a/Spanner/tests/System/PgTransactionTest.php b/Spanner/tests/System/PgTransactionTest.php index f6f807c33173..37fdbbb58430 100644 --- a/Spanner/tests/System/PgTransactionTest.php +++ b/Spanner/tests/System/PgTransactionTest.php @@ -48,7 +48,7 @@ public static function setUpTestFixtures(): void } parent::setUpTestFixtures(); - self::$tableName = "transactions_test"; + self::$tableName = 'transactions_test'; self::$database->updateDdlBatch([ 'CREATE TABLE ' . self::$tableName . ' ( diff --git a/Spanner/tests/System/PgWriteTest.php b/Spanner/tests/System/PgWriteTest.php index 037efa24ead8..395f154d55a5 100644 --- a/Spanner/tests/System/PgWriteTest.php +++ b/Spanner/tests/System/PgWriteTest.php @@ -25,9 +25,9 @@ use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\Date; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\PgNumeric; use Google\Cloud\Spanner\PgJsonb; +use Google\Cloud\Spanner\PgNumeric; +use Google\Cloud\Spanner\Timestamp; use Google\Rpc\Code; /** @@ -96,7 +96,7 @@ public function fieldValueProvider() [$this->randId(), 'datefield', new Date(new \DateTime('1981-01-20'))], [$this->randId(), 'intfield', 787878787], [$this->randId(), 'stringfield', 'foo bar'], - [$this->randId(), 'timestampfield', new Timestamp(new \DateTime)], + [$this->randId(), 'timestampfield', new Timestamp(new \DateTime())], [$this->randId(), 'pgnumericfield', new PgNumeric('0.123456789')], [$this->randId(), 'pgjsonbfield', new PgJsonb('{}')], [$this->randId(), 'pgjsonbfield', new PgJsonb('{"a": 1.1, "b": "def"}')], @@ -240,9 +240,9 @@ public function arrayFieldValueProvider() { return [ [$this->randId(), 'arrayfield', []], - [$this->randId(), 'arrayfield', [1,2,null,4,5]], + [$this->randId(), 'arrayfield', [1, 2, null, 4, 5]], [$this->randId(), 'arrayfield', null], - [$this->randId(), 'arrayboolfield', [true,false]], + [$this->randId(), 'arrayboolfield', [true, false]], [$this->randId(), 'arrayboolfield', []], [$this->randId(), 'arrayboolfield', [true, false, null, false]], [$this->randId(), 'arrayboolfield', null], @@ -254,9 +254,9 @@ public function arrayFieldValueProvider() [$this->randId(), 'arrayfloat4field', []], [$this->randId(), 'arrayfloat4field', [1.1, null, 1.3]], [$this->randId(), 'arrayfloat4field', null], - [$this->randId(), 'arraystringfield', ['foo','bar','baz']], + [$this->randId(), 'arraystringfield', ['foo', 'bar', 'baz']], [$this->randId(), 'arraystringfield', []], - [$this->randId(), 'arraystringfield', ['foo',null,'baz']], + [$this->randId(), 'arraystringfield', ['foo', null, 'baz']], [$this->randId(), 'arraystringfield', null], [$this->randId(), 'arraybytesfield', []], [$this->randId(), 'arraybytesfield', null], @@ -309,12 +309,12 @@ public function testWriteAndReadBackArrayValue($id, $field, $value) public function arrayFieldComplexValueProvider() { return [ - [$this->randId(), 'arraybytesfield', [new Bytes('foo'),null,new Bytes('baz')]], - [$this->randId(), 'arraytimestampfield', [new Timestamp(new \DateTime),null,new Timestamp(new \DateTime)]], - [$this->randId(), 'arraydatefield', [new Date(new \DateTime),null,new Date(new \DateTime)]], - [$this->randId(), 'arraypgnumericfield', [new PgNumeric("0.12345"),null,new PgNumeric("12345")]], - [$this->randId(), 'arraypgjsonbfield', [new PgJsonb('{"a":1.1,"b":"hello"}'),null, - new PgJsonb(["a" => 1, "b" => null]),new PgJsonb('{}'),new PgJsonb([])]], + [$this->randId(), 'arraybytesfield', [new Bytes('foo'), null, new Bytes('baz')]], + [$this->randId(), 'arraytimestampfield', [new Timestamp(new \DateTime()), null, new Timestamp(new \DateTime())]], + [$this->randId(), 'arraydatefield', [new Date(new \DateTime()), null, new Date(new \DateTime())]], + [$this->randId(), 'arraypgnumericfield', [new PgNumeric('0.12345'), null, new PgNumeric('12345')]], + [$this->randId(), 'arraypgjsonbfield', [new PgJsonb('{"a":1.1,"b":"hello"}'), null, + new PgJsonb(['a' => 1, 'b' => null]), new PgJsonb('{}'), new PgJsonb([])]], ]; } @@ -440,11 +440,11 @@ public function testWriteAndReadBackRandomNumeric($id, $numeric) public function randomNumericProvider() { return [ - [$this->randId(), new PgNumeric((string)rand(100, 9999))], - [$this->randId(), new PgNumeric((string)rand(100, 9999))], - [$this->randId(), new PgNumeric((string)rand(100, 9999))], - [$this->randId(), new PgNumeric((string)rand(100, 9999))], - [$this->randId(), new PgNumeric((string)rand(100, 9999))], + [$this->randId(), new PgNumeric((string) rand(100, 9999))], + [$this->randId(), new PgNumeric((string) rand(100, 9999))], + [$this->randId(), new PgNumeric((string) rand(100, 9999))], + [$this->randId(), new PgNumeric((string) rand(100, 9999))], + [$this->randId(), new PgNumeric((string) rand(100, 9999))], ]; } @@ -456,7 +456,7 @@ public function testCommitTimestamp() $id = $this->randId(); $ts = self::$database->insert(self::COMMIT_TIMESTAMP_TABLE_NAME, [ 'id' => $id, - 'committimestamp' => new CommitTimestamp + 'committimestamp' => new CommitTimestamp() ]); $res = self::$database->execute('SELECT * FROM ' . self::COMMIT_TIMESTAMP_TABLE_NAME . ' WHERE id = $1', [ @@ -581,7 +581,7 @@ public function testTimestampPrecisionLocale($timestamp) public function timestamps() { - $today = new \DateTime; + $today = new \DateTime(); $str = $today->format('Y-m-d\TH:i:s'); $todayLowMs = \DateTime::createFromFormat('U.u', time() . '.012345'); @@ -609,17 +609,17 @@ public function testExecuteUpdate() $db = self::$database; $db->runTransaction(function ($t) use ($id, $randStr) { - $count = $t->executeUpdate( - 'INSERT INTO ' . self::TABLE_NAME . ' (id, stringfield) VALUES ($1, $2)', - [ - 'parameters' => [ - 'p1' => $id, - 'p2' => $randStr - ] + $count = $t->executeUpdate( + 'INSERT INTO ' . self::TABLE_NAME . ' (id, stringfield) VALUES ($1, $2)', + [ + 'parameters' => [ + 'p1' => $id, + 'p2' => $randStr ] - ); + ] + ); - $this->assertEquals(1, $count); + $this->assertEquals(1, $count); $row = $t->execute('SELECT * FROM ' . self::TABLE_NAME . ' WHERE id = $1', [ 'parameters' => [ diff --git a/Spanner/tests/System/QueryTest.php b/Spanner/tests/System/QueryTest.php index 3e6f5a68c978..baaf5c80ce1f 100644 --- a/Spanner/tests/System/QueryTest.php +++ b/Spanner/tests/System/QueryTest.php @@ -83,7 +83,7 @@ public function testQueryReturnsArrayStruct() $res = $db->execute('SELECT ARRAY(SELECT STRUCT(1, 2))'); $row = $res->rows()->current(); - $this->assertEquals($row[0][0], [1,2]); + $this->assertEquals($row[0][0], [1, 2]); } /** @@ -250,7 +250,7 @@ public function testBindNumericParameter() $row = $res->rows()->current(); $this->assertInstanceOf(Numeric::class, $row['foo']); $this->assertEquals($str, $numeric->formatAsString()); - $this->assertEquals($str, (string)$numeric->get()); + $this->assertEquals($str, (string) $numeric->get()); } public function testBindNumericParameterNull() @@ -289,7 +289,7 @@ public function testBindBytesParameter() $row = $res->rows()->current(); $this->assertInstanceOf(Bytes::class, $row['foo']); $this->assertEquals($str, base64_decode($bytes->formatAsString())); - $this->assertEquals($str, (string)$bytes->get()); + $this->assertEquals($str, (string) $bytes->get()); } /** @@ -319,7 +319,7 @@ public function testBindDateParameter() { $db = self::$database; - $ts = new Date(new \DateTimeImmutable); + $ts = new Date(new \DateTimeImmutable()); $res = $db->execute('SELECT @param as foo', [ 'parameters' => [ @@ -609,16 +609,16 @@ public function arrayTypes() { return [ // boolean (covers 37) - [[true,true,false]], + [[true, true, false]], // int64 (covers 40) - [[5,4,3,2,1]], + [[5, 4, 3, 2, 1]], // float64 (covers 43) [[3.14, 4.13, 1.43]], // string (covers 46) - [['hello','world','google','cloud']], + [['hello', 'world', 'google', 'cloud']], // bytes (covers 49) [ @@ -750,7 +750,7 @@ public function testBindStructParameter() 'p4' => 10 ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('userf', Database::TYPE_STRING) ->add('threadf', Database::TYPE_INT64) ] @@ -774,7 +774,7 @@ public function testBindNullStructParameter() 'structParam' => null ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('userf', Database::TYPE_STRING) ->add('threadf', Database::TYPE_INT64) ] @@ -800,10 +800,10 @@ public function testBindNestedStructParameter() ] ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add( 'structf', - (new StructType)->add('nestedf', Database::TYPE_STRING) + (new StructType())->add('nestedf', Database::TYPE_STRING) ) ] ]); @@ -824,10 +824,10 @@ public function testBindNullNestedStructParameter() 'structParam' => null ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add( 'structf', - (new StructType)->add('nestedf', Database::TYPE_STRING) + (new StructType())->add('nestedf', Database::TYPE_STRING) ) ] ]); @@ -847,7 +847,7 @@ public function testBindEmptyStructParameter() 'structParam' => [] ], 'types' => [ - 'structParam' => new StructType + 'structParam' => new StructType() ] ]); @@ -867,7 +867,7 @@ public function testBindStructNoFieldsParameter() 'structParam' => null ], 'types' => [ - 'structParam' => new StructType + 'structParam' => new StructType() ] ]); @@ -889,7 +889,7 @@ public function testBindStructParameterNullFields() ] ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('f1', Database::TYPE_INT64) ] ]); @@ -912,7 +912,7 @@ public function testBindStructParameterEqualityCheck() ] ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('threadf', Database::TYPE_INT64) ->add('userf', Database::TYPE_STRING) ] @@ -936,7 +936,7 @@ public function testBindStructParameterNullCheck() ] ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('userf', Database::TYPE_STRING) ->add('threadf', Database::TYPE_INT64) ] @@ -964,9 +964,9 @@ public function testBindArrayOfStructsParameter() ], ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('arraysf', new ArrayType( - (new StructType)->add('threadid', Database::TYPE_INT64) + (new StructType())->add('threadid', Database::TYPE_INT64) )) ] ]); @@ -989,9 +989,9 @@ public function testBindArrayOfStructsNullParameter() ], ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('arraysf', new ArrayType( - (new StructType)->add('threadid', Database::TYPE_INT64) + (new StructType())->add('threadid', Database::TYPE_INT64) )) ] ]); @@ -1011,9 +1011,9 @@ public function testBindNullArrayOfStructsParameter() 'structParam' => null ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('arraysf', new ArrayType( - (new StructType)->add('threadid', Database::TYPE_INT64) + (new StructType())->add('threadid', Database::TYPE_INT64) )) ] ]); @@ -1030,13 +1030,13 @@ public function testBindArrayOfStructsDuplicateFieldName() $db = self::$database; $res = $db->execute('SELECT * FROM UNNEST(ARRAY(SELECT @structParam))', [ 'parameters' => [ - 'structParam' => (new StructValue) + 'structParam' => (new StructValue()) ->add('hello', 'world') ->add('foo', 'bar') ->add('foo', 2) ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('hello', Database::TYPE_STRING) ->add('foo', Database::TYPE_STRING) ->add('foo', Database::TYPE_INT64) @@ -1067,15 +1067,15 @@ public function testBindStructWithMixedUnnamedParameters() $db = self::$database; $res = $db->execute('SELECT * FROM UNNEST(ARRAY(SELECT @structParam))', [ 'parameters' => [ - 'structParam' => (new StructValue) + 'structParam' => (new StructValue()) ->addUnnamed(1) ->add('f1', 2) ->addUnnamed([ - 'a','b','c' + 'a', 'b', 'c' ]) ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->addUnnamed(Database::TYPE_INT64) ->add('f1', Database::TYPE_INT64) ->addUnnamed(new ArrayType(Database::TYPE_STRING)) @@ -1085,7 +1085,7 @@ public function testBindStructWithMixedUnnamedParameters() $this->assertEquals(1, $res[0]); $this->assertEquals(2, $res['f1']); $this->assertEquals([ - 'a','b','c' + 'a', 'b', 'c' ], $res[2]); } @@ -1097,16 +1097,16 @@ public function testBindStructWithAllUnnamedParameters() $db = self::$database; $res = $db->execute('SELECT * FROM UNNEST(ARRAY(SELECT @structParam))', [ 'parameters' => [ - 'structParam' => (new StructValue) + 'structParam' => (new StructValue()) ->addUnnamed(1) ->addUnnamed('field') ->addUnnamed([ - 'a','b','c' + 'a', 'b', 'c' ]) ->addUnnamed(false) ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->addUnnamed(Database::TYPE_INT64) ->addUnnamed(Database::TYPE_STRING) ->addUnnamed(new ArrayType(Database::TYPE_STRING)) @@ -1117,7 +1117,7 @@ public function testBindStructWithAllUnnamedParameters() $this->assertEquals(1, $res[0]); $this->assertEquals('field', $res[1]); $this->assertEquals([ - 'a','b','c' + 'a', 'b', 'c' ], $res[2]); $this->assertFalse($res[3]); } @@ -1136,7 +1136,7 @@ public function testBindStructInferredParameterTypes() 'structParam' => $values ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('str', Database::TYPE_STRING) ] ])->rows()->current(); @@ -1155,14 +1155,14 @@ public function testBindStructInferredParameterTypesWithUnnamed() $db = self::$database; $res = $db->execute('SELECT * FROM UNNEST(ARRAY(SELECT @structParam))', [ 'parameters' => [ - 'structParam' => (new StructValue) + 'structParam' => (new StructValue()) ->add('arr', ['a', 'b']) ->addUnnamed('hello') ->addUnnamed(10) ->add('str', 'world') ], 'types' => [ - 'structParam' => (new StructType) + 'structParam' => (new StructType()) ->add('str', Database::TYPE_STRING) ] ])->rows(Result::RETURN_NAME_VALUE_PAIR)->current(); diff --git a/Spanner/tests/System/SessionTest.php b/Spanner/tests/System/SessionTest.php index 6fe694880053..c75345707ed1 100644 --- a/Spanner/tests/System/SessionTest.php +++ b/Spanner/tests/System/SessionTest.php @@ -38,7 +38,7 @@ public function testCacheSessionPool() $identity['database'] ); - $cache = new MemoryCacheItemPool; + $cache = new MemoryCacheItemPool(); $pool = new CacheSessionPool($cache, [ 'maxSessions' => 10, 'minSessions' => 5, @@ -105,7 +105,7 @@ public function testSessionPoolShouldFailWhenIncorrectDatabase() ['maxCyclesToWaitForSession' => 1] ); $db->runTransaction(function ($t) { - $t->select("SELECT 1"); + $t->select('SELECT 1'); $t->commit(); }); } diff --git a/Spanner/tests/System/SnapshotTest.php b/Spanner/tests/System/SnapshotTest.php index f4c11db9d344..253d9087eb8b 100644 --- a/Spanner/tests/System/SnapshotTest.php +++ b/Spanner/tests/System/SnapshotTest.php @@ -90,7 +90,7 @@ public function testSnapshotExactTimestampRead() $db->insert(self::$tableName, $row); sleep(1); - $ts = new Timestamp(new \DateTimeImmutable); + $ts = new Timestamp(new \DateTimeImmutable()); sleep(1); $newRow = $row; @@ -155,7 +155,7 @@ public function testSnapshotExactStaleness() $db->insert(self::$tableName, $row); sleep(1); - $ts = new Timestamp(new \DateTimeImmutable); + $ts = new Timestamp(new \DateTimeImmutable()); sleep(1); $newRow = $row; @@ -190,7 +190,7 @@ public function testSnapshotMaxStaleness() $db->insert(self::$tableName, $row); sleep(1); - $ts = new Timestamp(new \DateTimeImmutable); + $ts = new Timestamp(new \DateTimeImmutable()); sleep(1); $newRow = $row; @@ -219,7 +219,7 @@ public function testSnapshotMinReadTimestampFails() $db = self::$database; $db->snapshot([ - 'minReadTimestamp' => new Timestamp(new \DateTimeImmutable) + 'minReadTimestamp' => new Timestamp(new \DateTimeImmutable()) ]); } diff --git a/Spanner/tests/System/SpannerPgTestCase.php b/Spanner/tests/System/SpannerPgTestCase.php index 5787afd0a69a..79426b24bc6c 100644 --- a/Spanner/tests/System/SpannerPgTestCase.php +++ b/Spanner/tests/System/SpannerPgTestCase.php @@ -17,12 +17,12 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Auth\Cache\MemoryCacheItemPool; use Google\Cloud\Core\Testing\System\SystemTestCase; use Google\Cloud\Spanner; -use Google\Cloud\Spanner\SpannerClient; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; use Google\Cloud\Spanner\Session\CacheSessionPool; -use Google\Auth\Cache\MemoryCacheItemPool; +use Google\Cloud\Spanner\SpannerClient; /** * @group spanner @@ -106,7 +106,7 @@ public static function getDatabaseFromInstance($instance, $dbName, $options = [] public static function getDatabaseWithSessionPool($dbName, $options = []) { - $sessionCache = new MemoryCacheItemPool; + $sessionCache = new MemoryCacheItemPool(); $sessionPool = new CacheSessionPool( $sessionCache, $options @@ -128,7 +128,7 @@ public static function getDatabaseInstance($dbName) public static function skipEmulatorTests() { - if ((bool) getenv("SPANNER_EMULATOR_HOST")) { + if ((bool) getenv('SPANNER_EMULATOR_HOST')) { self::markTestSkipped('This test is not supported by the emulator.'); } } @@ -183,7 +183,7 @@ private static function getClient() $clientConfig['gapicSpannerInstanceAdminClient'] = new Spanner\Admin\Instance\V1\InstanceAdminClient($gapicConfig); - echo "Using Service Address: ". $serviceAddress . PHP_EOL; + echo 'Using Service Address: ' . $serviceAddress . PHP_EOL; } self::$client = new SpannerClient($clientConfig); diff --git a/Spanner/tests/System/SpannerTestCase.php b/Spanner/tests/System/SpannerTestCase.php index d74467a5237f..c3cc4687771d 100644 --- a/Spanner/tests/System/SpannerTestCase.php +++ b/Spanner/tests/System/SpannerTestCase.php @@ -17,12 +17,12 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Auth\Cache\MemoryCacheItemPool; use Google\Cloud\Core\Testing\System\SystemTestCase; use Google\Cloud\Spanner; -use Google\Cloud\Spanner\SpannerClient; -use Google\Cloud\Spanner\Session\CacheSessionPool; -use Google\Auth\Cache\MemoryCacheItemPool; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; +use Google\Cloud\Spanner\Session\CacheSessionPool; +use Google\Cloud\Spanner\SpannerClient; /** * @group spanner @@ -125,7 +125,7 @@ private static function getClient() $clientConfig['gapicSpannerInstanceAdminClient'] = new Spanner\Admin\Instance\V1\InstanceAdminClient($gapicConfig); - echo "Using Service Address: ". $serviceAddress . PHP_EOL; + echo 'Using Service Address: ' . $serviceAddress . PHP_EOL; } self::$client = new SpannerClient($clientConfig); @@ -146,7 +146,7 @@ public static function getDatabaseFromInstance($instance, $dbName, $options = [] public static function getDatabaseWithSessionPool($dbName, $options = []) { - $sessionCache = new MemoryCacheItemPool; + $sessionCache = new MemoryCacheItemPool(); $sessionPool = new CacheSessionPool( $sessionCache, $options @@ -163,7 +163,7 @@ public static function getDatabaseWithSessionPool($dbName, $options = []) public static function skipEmulatorTests() { - if ((bool) getenv("SPANNER_EMULATOR_HOST")) { + if ((bool) getenv('SPANNER_EMULATOR_HOST')) { self::markTestSkipped('This test is not supported by the emulator.'); } } diff --git a/Spanner/tests/System/TransactionTest.php b/Spanner/tests/System/TransactionTest.php index 99b9931cb025..968dc122d6b0 100644 --- a/Spanner/tests/System/TransactionTest.php +++ b/Spanner/tests/System/TransactionTest.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Tests\System; +use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Spanner\Date; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; @@ -79,7 +79,7 @@ public function testRunTransaction() $row = [ 'id' => $id, 'name' => uniqid(self::TESTING_PREFIX), - 'birthday' => new Date(new \DateTime) + 'birthday' => new Date(new \DateTime()) ]; $cols = array_keys($row); @@ -114,7 +114,6 @@ public function testConcurrentTransactionsIncrementValueWithRead() 'number' => 0 ]); - $iterations = shell_exec(implode(' ', [ 'php', __DIR__ . '/pcntl/ConcurrentTransactionsIncrementValueWithRead.php', @@ -395,7 +394,7 @@ public function testRunTransactionILBWithMultipleOperations() $row = [ 'id' => $id, 'name' => uniqid(self::TESTING_PREFIX), - 'birthday' => new Date(new \DateTime) + 'birthday' => new Date(new \DateTime()) ]; // Representative of all mutations $t->insert(self::TEST_TABLE_NAME, $row); @@ -408,7 +407,7 @@ public function testRunTransactionILBWithMultipleOperations() 'parameters' => [ 'id' => $id, 'name' => uniqid(self::TESTING_PREFIX), - 'birthday' => new Date(new \DateTime) + 'birthday' => new Date(new \DateTime()) ] ] ); diff --git a/Spanner/tests/System/WriteTest.php b/Spanner/tests/System/WriteTest.php index e6724b7111f6..496177915e3c 100644 --- a/Spanner/tests/System/WriteTest.php +++ b/Spanner/tests/System/WriteTest.php @@ -25,8 +25,8 @@ use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\Date; use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Numeric; +use Google\Cloud\Spanner\Timestamp; use Google\Rpc\Code; /** @@ -83,7 +83,7 @@ public function fieldValueProvider() return [ [$this->randId(), 'boolField', false], [$this->randId(), 'boolField', true], - [$this->randId(), 'arrayField', [1,2,3,4,5]], + [$this->randId(), 'arrayField', [1, 2, 3, 4, 5]], [$this->randId(), 'dateField', new Date(new \DateTime('1981-01-20'))], [$this->randId(), 'floatField', 3.1415], [$this->randId(), 'floatField', INF], @@ -93,7 +93,7 @@ public function fieldValueProvider() [$this->randId(), 'float32Field', -INF], [$this->randId(), 'intField', 787878787], [$this->randId(), 'stringField', 'foo bar'], - [$this->randId(), 'timestampField', new Timestamp(new \DateTime)], + [$this->randId(), 'timestampField', new Timestamp(new \DateTime())], [$this->randId(), 'numericField', new Numeric('0.123456789')] ]; } @@ -255,9 +255,9 @@ public function arrayFieldValueProvider() { return [ [$this->randId(), 'arrayField', []], - [$this->randId(), 'arrayField', [1,2,null,4,5]], + [$this->randId(), 'arrayField', [1, 2, null, 4, 5]], [$this->randId(), 'arrayField', null], - [$this->randId(), 'arrayBoolField', [true,false]], + [$this->randId(), 'arrayBoolField', [true, false]], [$this->randId(), 'arrayBoolField', []], [$this->randId(), 'arrayBoolField', [true, false, null, false]], [$this->randId(), 'arrayBoolField', null], @@ -269,9 +269,9 @@ public function arrayFieldValueProvider() [$this->randId(), 'arrayFloat32Field', []], [$this->randId(), 'arrayFloat32Field', [1.1, null, 1.3]], [$this->randId(), 'arrayFloat32Field', null], - [$this->randId(), 'arrayStringField', ['foo','bar','baz']], + [$this->randId(), 'arrayStringField', ['foo', 'bar', 'baz']], [$this->randId(), 'arrayStringField', []], - [$this->randId(), 'arrayStringField', ['foo',null,'baz']], + [$this->randId(), 'arrayStringField', ['foo', null, 'baz']], [$this->randId(), 'arrayStringField', null], [$this->randId(), 'arrayBytesField', []], [$this->randId(), 'arrayBytesField', null], @@ -343,10 +343,10 @@ public function testWriteAndReadBackFancyArrayValue($id, $field, $value) public function arrayFieldComplexValueProvider() { return [ - [$this->randId(), 'arrayBytesField', [new Bytes('foo'),null,new Bytes('baz')]], - [$this->randId(), 'arrayTimestampField', [new Timestamp(new \DateTime),null,new Timestamp(new \DateTime)]], - [$this->randId(), 'arrayDateField', [new Date(new \DateTime),null,new Date(new \DateTime)]], - [$this->randId(), 'arrayNumericField', [new Numeric("0.12345"),null,new NUMERIC("12345")]], + [$this->randId(), 'arrayBytesField', [new Bytes('foo'), null, new Bytes('baz')]], + [$this->randId(), 'arrayTimestampField', [new Timestamp(new \DateTime()), null, new Timestamp(new \DateTime())]], + [$this->randId(), 'arrayDateField', [new Date(new \DateTime()), null, new Date(new \DateTime())]], + [$this->randId(), 'arrayNumericField', [new Numeric('0.12345'), null, new NUMERIC('12345')]], ]; } @@ -480,11 +480,11 @@ public function randomNumericProvider() } return [ - [$this->randId(), new Numeric((string)rand(100, 9999))], - [$this->randId(), new Numeric((string)rand(100, 9999))], - [$this->randId(), new Numeric((string)rand(100, 9999))], - [$this->randId(), new Numeric((string)rand(100, 9999))], - [$this->randId(), new Numeric((string)rand(100, 9999))], + [$this->randId(), new Numeric((string) rand(100, 9999))], + [$this->randId(), new Numeric((string) rand(100, 9999))], + [$this->randId(), new Numeric((string) rand(100, 9999))], + [$this->randId(), new Numeric((string) rand(100, 9999))], + [$this->randId(), new Numeric((string) rand(100, 9999))], ]; } @@ -496,7 +496,7 @@ public function testCommitTimestamp() $id = $this->randId(); $ts = self::$database->insert(self::COMMIT_TIMESTAMP_TABLE_NAME, [ 'id' => $id, - 'commitTimestamp' => new CommitTimestamp + 'commitTimestamp' => new CommitTimestamp() ]); $res = self::$database->execute('SELECT * FROM ' . self::COMMIT_TIMESTAMP_TABLE_NAME . ' WHERE id = @id', [ @@ -621,7 +621,7 @@ public function testTimestampPrecisionLocale($timestamp) public function timestamps() { - $today = new \DateTime; + $today = new \DateTime(); $str = $today->format('Y-m-d\TH:i:s'); $todayLowMs = \DateTime::createFromFormat('U.u', time() . '.012345'); @@ -649,17 +649,17 @@ public function testExecuteUpdate() $db = self::$database; $db->runTransaction(function ($t) use ($id, $randStr) { - $count = $t->executeUpdate( - 'INSERT INTO ' . self::TABLE_NAME . ' (id, stringField) VALUES (@id, @string)', - [ - 'parameters' => [ - 'id' => $id, - 'string' => $randStr - ] + $count = $t->executeUpdate( + 'INSERT INTO ' . self::TABLE_NAME . ' (id, stringField) VALUES (@id, @string)', + [ + 'parameters' => [ + 'id' => $id, + 'string' => $randStr ] - ); + ] + ); - $this->assertEquals(1, $count); + $this->assertEquals(1, $count); $row = $t->execute('SELECT * FROM ' . self::TABLE_NAME . ' WHERE id = @id', [ 'parameters' => [ diff --git a/Spanner/tests/System/pcntl/AbortedErrorCausesRetry.php b/Spanner/tests/System/pcntl/AbortedErrorCausesRetry.php index 590cd55fd0a2..9dfbb3639f86 100644 --- a/Spanner/tests/System/pcntl/AbortedErrorCausesRetry.php +++ b/Spanner/tests/System/pcntl/AbortedErrorCausesRetry.php @@ -6,7 +6,7 @@ use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Spanner\Tests\System\SpannerTestCase; -list ($dbName, $tableName, $id) = getInputArgs(); +list($dbName, $tableName, $id) = getInputArgs(); $delay = 5000; if ($childPID1 = pcntl_fork()) { diff --git a/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithExecute.php b/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithExecute.php index 7f14099acacd..37b8cba7c27f 100644 --- a/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithExecute.php +++ b/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithExecute.php @@ -5,7 +5,7 @@ use Google\Cloud\Spanner\Tests\System\SpannerTestCase; -list ($dbName, $tableName, $id) = getInputArgs(); +list($dbName, $tableName, $id) = getInputArgs(); $tmpFile = sys_get_temp_dir() . '/ConcurrentTransactionsIncremementValueWithExecute.txt'; setupIterationTracker($tmpFile); @@ -22,7 +22,7 @@ ] ])->rows()->current(); - $row['number'] +=1; + $row['number'] += 1; $transaction->update($tableName, $row); $transaction->commit(); diff --git a/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithRead.php b/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithRead.php index 7fd596ac369f..5355d50bcc15 100644 --- a/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithRead.php +++ b/Spanner/tests/System/pcntl/ConcurrentTransactionsIncrementValueWithRead.php @@ -6,13 +6,13 @@ use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Tests\System\SpannerTestCase; -list ($dbName, $tableName, $id) = getInputArgs(); +list($dbName, $tableName, $id) = getInputArgs(); $tmpFile = sys_get_temp_dir() . '/ConcurrentTransactionsIncremementValueWithRead.txt'; setupIterationTracker($tmpFile); $keyset = new KeySet(['keys' => [$id]]); -$columns = ['id','number']; +$columns = ['id', 'number']; $callable = function ($dbName, KeySet $keyset, array $columns, $tableName) use ($tmpFile) { $iterations = 0; @@ -21,7 +21,7 @@ $iterations++; $row = $transaction->read($tableName, $keyset, $columns)->rows()->current(); - $row['number'] +=1; + $row['number'] += 1; $transaction->update($tableName, $row); $transaction->commit(); diff --git a/Spanner/tests/Unit/ArrayTypeTest.php b/Spanner/tests/Unit/ArrayTypeTest.php index d04b0f2d7fd2..b24199bfd6aa 100644 --- a/Spanner/tests/Unit/ArrayTypeTest.php +++ b/Spanner/tests/Unit/ArrayTypeTest.php @@ -72,7 +72,7 @@ public function testArrayType($type) public function testArrayTypeStruct() { - $struct = new StructType; + $struct = new StructType(); $struct->add('foo', Database::TYPE_STRING); $arr = new ArrayType($struct); diff --git a/Spanner/tests/Unit/BackupTest.php b/Spanner/tests/Unit/BackupTest.php index 9dfe5f9b4c1e..6cf6f822febb 100644 --- a/Spanner/tests/Unit/BackupTest.php +++ b/Spanner/tests/Unit/BackupTest.php @@ -17,19 +17,19 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use DateTime; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Google\ApiCore\OperationResponse; use Google\ApiCore\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; -use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; @@ -38,7 +38,6 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use DateTime; /** * @group spanner @@ -97,8 +96,8 @@ public function setUp(): void $this->operationResponse->withResultFunction(Argument::type('callable')) ->willReturn($this->operationResponse->reveal()); - $this->expireTime = new DateTime("+7 hours"); - $this->versionTime = new DateTime("-2 hours"); + $this->expireTime = new DateTime('+7 hours'); + $this->versionTime = new DateTime('-2 hours'); } public function testName() @@ -327,7 +326,7 @@ public function testExists() public function testUpdateExpireTime() { - $newExpireTime = new DateTime("+1 day"); + $newExpireTime = new DateTime('+1 day'); $response = new BackupProto([ 'name' => 'foo', diff --git a/Spanner/tests/Unit/Batch/BatchClientTest.php b/Spanner/tests/Unit/Batch/BatchClientTest.php index b95d35cf4901..1f37dfbdfa16 100644 --- a/Spanner/tests/Unit/Batch/BatchClientTest.php +++ b/Spanner/tests/Unit/Batch/BatchClientTest.php @@ -18,26 +18,25 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; use Google\ApiCore\Serializer; -use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Timestamp; use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; -use InvalidArgumentException; -use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use Google\Cloud\Spanner\V1\BeginTransactionRequest; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; use Google\Cloud\Spanner\V1\CreateSessionRequest; use Google\Cloud\Spanner\V1\Session; use Google\Cloud\Spanner\V1\Transaction; use Google\Protobuf\Timestamp as TimestampProto; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; +use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; /** * @group spanner @@ -57,7 +56,6 @@ class BatchClientTest extends TestCase private $spannerClient; private $serializer; private $batchClient; - private $spannerClient; public function setUp(): void { @@ -155,7 +153,7 @@ public function testReadPartitionFromString() $token = 'foobar'; $table = 'table'; $keyset = new KeySet(['all' => true]); - $columns = ['a','b']; + $columns = ['a', 'b']; $options = ['hello' => 'world']; $partition = new ReadPartition($token, $table, $keyset, $columns, $options); diff --git a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php index a0f3a05453c9..54f7018526b0 100644 --- a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php @@ -18,7 +18,6 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; use Google\ApiCore\Serializer; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\PartitionInterface; @@ -32,11 +31,11 @@ use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\ExecuteSqlRequest; -use Google\Cloud\Spanner\V1\PartitionReadRequest; -use Google\Cloud\Spanner\V1\PartitionQueryRequest; -use Google\Cloud\Spanner\V1\ReadRequest; use Google\Cloud\Spanner\V1\Partition; +use Google\Cloud\Spanner\V1\PartitionQueryRequest; +use Google\Cloud\Spanner\V1\PartitionReadRequest; use Google\Cloud\Spanner\V1\PartitionResponse; +use Google\Cloud\Spanner\V1\ReadRequest; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -61,7 +60,6 @@ class BatchSnapshotTest extends TestCase private $session; private $timestamp; private $snapshot; - private $spannerClient; public function setUp(): void { @@ -294,7 +292,7 @@ public function testExecutePartitionInvalidType() $this->expectException(\BadMethodCallException::class); $this->expectExceptionMessage('Unsupported partition type.'); - $dummy = new DummyPartition; + $dummy = new DummyPartition(); $this->snapshot->executePartition($dummy); } } @@ -302,8 +300,14 @@ public function testExecutePartitionInvalidType() //@codingStandardsIgnoreStart class DummyPartition implements PartitionInterface { - public function __toString() {} - public function serialize() {} - public static function hydrate(array $data) {} + public function __toString() + { + } + public function serialize() + { + } + public static function hydrate(array $data) + { + } } //@codingStandardsIgnoreEnd diff --git a/Spanner/tests/Unit/BytesTest.php b/Spanner/tests/Unit/BytesTest.php index e923a72c702e..104276ee2685 100644 --- a/Spanner/tests/Unit/BytesTest.php +++ b/Spanner/tests/Unit/BytesTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Spanner\Bytes; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\Bytes; use PHPUnit\Framework\TestCase; /** diff --git a/Spanner/tests/Unit/CommitTimestampTest.php b/Spanner/tests/Unit/CommitTimestampTest.php index 782de7af676f..9dd2fc9ef6a6 100644 --- a/Spanner/tests/Unit/CommitTimestampTest.php +++ b/Spanner/tests/Unit/CommitTimestampTest.php @@ -30,7 +30,7 @@ class CommitTimestampTest extends TestCase public function setUp(): void { - $this->t = new CommitTimestamp; + $this->t = new CommitTimestamp(); } public function testType() diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index 1b5735c28756..611e215f7d90 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -18,29 +18,27 @@ namespace Google\Cloud\Spanner\Tests\Unit; use Google\ApiCore\OperationResponse; +use Google\ApiCore\Page; +use Google\ApiCore\PagedListResponse; use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; -use Google\ApiCore\PagedListResponse; -use Google\ApiCore\Page; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Exception\ServerException; +use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\Testing\Snippet\Fixtures; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Core\Testing\Snippet\Fixtures; +use Google\Cloud\Spanner\Admin\Database\V1\Backup; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; use Google\Cloud\Spanner\Admin\Database\V1\DatabaseDialect; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlResponse; use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest; -use Google\Cloud\Spanner\Admin\Database\V1\Backup; use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse; -use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlResponse; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; -use Google\Cloud\Spanner\Connection\ConnectionInterface; -use Google\Cloud\Spanner\Connection\Grpc; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\KeySet; @@ -52,42 +50,38 @@ use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\BatchWriteRequest; use Google\Cloud\Spanner\V1\BatchWriteRequest\MutationGroup; use Google\Cloud\Spanner\V1\BeginTransactionRequest; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\CommitRequest; use Google\Cloud\Spanner\V1\CommitResponse; -use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type; use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; use Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest; use Google\Cloud\Spanner\V1\ExecuteBatchDmlResponse; use Google\Cloud\Spanner\V1\ExecuteSqlRequest; use Google\Cloud\Spanner\V1\Mutation; -use Google\Cloud\Spanner\V1\ReadRequest; -use Google\Cloud\Spanner\V1\TransactionSelector; -use Google\Cloud\Spanner\V1\Transaction as TransactionProto; use Google\Cloud\Spanner\V1\PartialResultSet; +use Google\Cloud\Spanner\V1\ReadRequest; use Google\Cloud\Spanner\V1\ResultSet; use Google\Cloud\Spanner\V1\ResultSetMetadata; use Google\Cloud\Spanner\V1\ResultSetStats; use Google\Cloud\Spanner\V1\Session as SessionProto; use Google\Cloud\Spanner\V1\StructType; use Google\Cloud\Spanner\V1\StructType\Field; +use Google\Cloud\Spanner\V1\Transaction as TransactionProto; +use Google\Cloud\Spanner\V1\TransactionSelector; use Google\Cloud\Spanner\V1\Type as TypeProto; use Google\Protobuf\Duration; use Google\Protobuf\ListValue; use Google\Protobuf\Timestamp as TimestampProto; use Google\Protobuf\Value; -use Google\Protobuf\Internal\RepeatedField; -use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType; -use Google\Cloud\Spanner\V1\TransactionOptions; use Google\Rpc\Code; use Google\Rpc\Status; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Cloud\Core\Exception\ServiceException; -use Google\LongRunning\Client\OperationsClient; /** * @group spanner @@ -111,6 +105,29 @@ class DatabaseTest extends TestCase const TIMESTAMP = '2017-01-09T18:05:22.534799Z'; const BEGIN_RW_OPTIONS = ['begin' => ['readWrite' => []]]; + private const DIRECTED_READ_OPTIONS_INCLUDE_REPLICAS = [ + 'includeReplicas' => [ + 'autoFailoverDisabled' => false, + 'replicaSelections' => [ + [ + 'location' => 'us-central1', + 'type' => ReplicaType::READ_WRITE, + ] + ] + ] + ]; + + private const DIRECTED_READ_OPTIONS_EXCLUDE_REPLICAS = [ + 'excludeReplicas' => [ + 'replicaSelections' => [ + [ + 'location' => 'us-central1', + 'type' => ReplicaType::READ_WRITE, + ] + ] + ] + ]; + private $spannerClient; private $instanceAdminClient; private $databaseAdminClient; @@ -119,9 +136,7 @@ class DatabaseTest extends TestCase private $sessionPool; private $database; private $session; - private $directedReadOptionsIncludeReplicas; - private $directedReadOptionsExcludeReplicas; - + private $operationResponse; public function setUp(): void { @@ -141,6 +156,7 @@ public function setUp(): void return $this->formatTimestampFromApi($v); } ]); + $this->sessionPool = $this->prophesize(SessionPoolInterface::class); $this->spannerClient = $this->prophesize(SpannerClient::class); $this->instanceAdminClient = $this->prophesize(InstanceAdminClient::class); @@ -155,30 +171,6 @@ public function setUp(): void self::SESSION ); - $this->directedReadOptionsIncludeReplicas = [ - 'includeReplicas' => [ - 'autoFailoverDisabled' => false, - 'replicaSelections' => [ - [ - 'location' => 'us-central1', - 'type' => ReplicaType::READ_WRITE, - - ] - ] - ] - ]; - $this->directedReadOptionsExcludeReplicas = [ - 'excludeReplicas' => [ - // 'autoFailoverDisabled' => false, - 'replicaSelections' => [ - [ - 'location' => 'us-central1', - 'type' => ReplicaType::READ_WRITE, - ] - ] - ] - ]; - $this->instance = new Instance( $this->spannerClient->reveal(), $this->instanceAdminClient->reveal(), @@ -188,7 +180,7 @@ public function setUp(): void self::INSTANCE, false, [], - ['directedReadOptions' => $this->directedReadOptionsIncludeReplicas] + ['directedReadOptions' => self::DIRECTED_READ_OPTIONS_INCLUDE_REPLICAS] ); $this->sessionPool->acquire(Argument::type('string')) @@ -214,7 +206,6 @@ public function setUp(): void $this->operationResponse = $this->prophesize(OperationResponse::class); $this->operationResponse->withResultFunction(Argument::type('callable')) ->willReturn($this->operationResponse->reveal()); - } public function testName() @@ -263,7 +254,6 @@ public function testState() $this->database->state(); } - public function testCreateBackup() { $expireTime = new \DateTime(); @@ -303,7 +293,7 @@ public function testBackups() $pagedListResponse->getPage() ->willReturn($page->reveal()); - $expectedFilter = "database:".$this->database->name(); + $expectedFilter = 'database:' . $this->database->name(); $this->databaseAdminClient->listBackups( Argument::that(function ($request) use ($expectedFilter) { $message = $this->serializer->encodeMessage($request); @@ -340,8 +330,8 @@ public function testBackupsWithCustomFilter() $pagedListResponse->getPage() ->willReturn($page->reveal()); - $defaultFilter = "database:" . $this->database->name(); - $customFilter = "customFilter"; + $defaultFilter = 'database:' . $this->database->name(); + $customFilter = 'customFilter'; $expectedFilter = sprintf('(%1$s) AND (%2$s)', $defaultFilter, $customFilter); $this->databaseAdminClient->listBackups( @@ -494,8 +484,7 @@ public function testCreatePostgresDialect() $createStatement = sprintf('CREATE DATABASE "%s"', self::DATABASE); $this->databaseAdminClient->createDatabase( - Argument::that( - function ($request) use ($createStatement) { + Argument::that(function ($request) use ($createStatement) { $message = $this->serializer->encodeMessage($request); $this->assertEquals($message['createStatement'], $createStatement); $this->assertEmpty($message['extraStatements']); @@ -507,7 +496,7 @@ function ($request) use ($createStatement) { ->willReturn($this->operationResponse->reveal()); $op = $this->database->create([ - 'databaseDialect'=> DatabaseDialect::POSTGRESQL + 'databaseDialect' => DatabaseDialect::POSTGRESQL ]); $this->assertInstanceOf(OperationResponse::class, $op); @@ -521,8 +510,7 @@ public function testRestoreFromBackupName() $backupName = DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP); $this->databaseAdminClient->restoreDatabase( - Argument::that( - function ($request) use ($backupName) { + Argument::that(function ($request) use ($backupName) { $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['parent'], @@ -549,8 +537,7 @@ public function testRestoreFromBackupObject() $backupObj = $this->instance->backup(self::BACKUP); $this->databaseAdminClient->restoreDatabase( - Argument::that( - function ($request) use ($backupObj) { + Argument::that(function ($request) use ($backupObj) { $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['parent'], @@ -576,8 +563,7 @@ public function testUpdateDdl() { $statement = 'foo'; $this->databaseAdminClient->updateDatabaseDdl( - Argument::that( - function ($request) use ($statement) { + Argument::that(function ($request) use ($statement) { $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['database'], @@ -603,8 +589,7 @@ public function testUpdateDdlBatch() $statements = ['foo', 'bar']; $this->databaseAdminClient->updateDatabaseDdl( - Argument::that( - function ($request) use ($statements) { + Argument::that(function ($request) use ($statements) { $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['database'], @@ -629,8 +614,7 @@ public function testUpdateWithSingleStatement() $statement = 'foo'; $this->databaseAdminClient->updateDatabaseDdl( - Argument::that( - function ($request) use ($statement) { + Argument::that(function ($request) use ($statement) { $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['database'], @@ -1692,7 +1676,7 @@ public function testExecuteWithDirectedRead() $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['directedReadOptions'], - $this->directedReadOptionsIncludeReplicas + self::DIRECTED_READ_OPTIONS_INCLUDE_REPLICAS ); return true; }), @@ -1715,7 +1699,7 @@ public function testPrioritizeExecuteDirectedReadOptions() $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['directedReadOptions'], - $this->directedReadOptionsExcludeReplicas + self::DIRECTED_READ_OPTIONS_EXCLUDE_REPLICAS ); return true; }), @@ -1727,7 +1711,7 @@ public function testPrioritizeExecuteDirectedReadOptions() $sql = 'SELECT * FROM Table'; $res = $this->database->execute( $sql, - ['directedReadOptions' => $this->directedReadOptionsExcludeReplicas] + ['directedReadOptions' => self::DIRECTED_READ_OPTIONS_EXCLUDE_REPLICAS] ); $this->assertInstanceOf(Result::class, $res); $rows = iterator_to_array($res->rows()); @@ -1744,7 +1728,7 @@ public function testReadWithDirectedRead() $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['directedReadOptions'], - $this->directedReadOptionsIncludeReplicas + self::DIRECTED_READ_OPTIONS_INCLUDE_REPLICAS ); return true; }), @@ -1773,7 +1757,7 @@ public function testPrioritizeReadDirectedReadOptions() $message = $this->serializer->encodeMessage($request); $this->assertEquals( $message['directedReadOptions'], - $this->directedReadOptionsExcludeReplicas + self::DIRECTED_READ_OPTIONS_EXCLUDE_REPLICAS ); return true; }), @@ -1786,7 +1770,7 @@ public function testPrioritizeReadDirectedReadOptions() $table, new KeySet(['keys' => $keys]), $columns, - ['directedReadOptions' => $this->directedReadOptionsExcludeReplicas] + ['directedReadOptions' => self::DIRECTED_READ_OPTIONS_EXCLUDE_REPLICAS] ); $this->assertInstanceOf(Result::class, $res); $rows = iterator_to_array($res->rows()); @@ -1922,10 +1906,15 @@ public function testRunTransactionWithUpdateBatchError() $message['requestOptions']['transactionTag'], self::TRANSACTION_TAG ); - $this->assertEquals( - $message['transaction'], - ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] - ); + $this->assertEquals([ + 'begin' => [ + 'readWrite' => [ + 'readLockMode' => 0, + 'multiplexedSessionPreviousTransactionId' => '', + ], + 'excludeTxnFromChangeStreams' => false + ] + ], $message['transaction']); return true; }), Argument::type('array') @@ -1966,8 +1955,14 @@ public function testRunTransactionWithFirstFailedStatement() $message['requestOptions']['transactionTag'], self::TRANSACTION_TAG ); - return $message['transaction'] == ['begin' => - ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false] + return $message['transaction'] == [ + 'begin' => [ + 'readWrite' => [ + 'readLockMode' => 0, + 'multiplexedSessionPreviousTransactionId' => '', + ], + 'excludeTxnFromChangeStreams' => false, + ] ]; }), Argument::type('array') @@ -2049,7 +2044,6 @@ public function testRunTransactionWithCommitAborted() return $commitResponse; }); - $this->database->runTransaction(function ($t) use ($sql) { $t->execute($sql); $t->commit(); @@ -2070,8 +2064,14 @@ public function testRunTransactionWithBeginTransactionFailure() $message['requestOptions']['transactionTag'], self::TRANSACTION_TAG ); - return $message['transaction'] == ['begin' => - ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false] + return $message['transaction'] == [ + 'begin' => [ + 'readWrite' => [ + 'readLockMode' => 0, + 'multiplexedSessionPreviousTransactionId' => '' + ], + 'excludeTxnFromChangeStreams' => false + ] ]; }), Argument::type('array') @@ -2116,7 +2116,6 @@ public function testRunTransactionWithUnavailableErrorRetry() { $sql = $this->createStreamingAPIArgs()['sql']; $numOfRetries = 2; - $unavailable = new ServiceException('Unavailable', 14); $result = $this->resultGeneratorStream( null, new ResultSetStats(['row_count_lower_bound' => 1]), @@ -2132,17 +2131,25 @@ public function testRunTransactionWithUnavailableErrorRetry() $this->assertEquals($message['sql'], $sql); $this->assertEquals( $message['transaction'], - ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] + [ + 'begin' => [ + 'readWrite' => [ + 'readLockMode' => 0, + 'multiplexedSessionPreviousTransactionId' => '' + ], + 'excludeTxnFromChangeStreams' => false + ] + ] ); return $message['requestOptions']['transactionTag'] == self::TRANSACTION_TAG; }), Argument::type('array') ) ->shouldBeCalledTimes($numOfRetries) - ->will(function () use (&$it, $unavailable, $numOfRetries, $result) { + ->will(function () use (&$it, $numOfRetries, $result) { $it++; if ($it < $numOfRetries) { - throw $unavailable; + throw new ServiceException('Unavailable', 14); } return $result; }); @@ -2215,7 +2222,15 @@ public function testRunTransactionWithUnavailableAndAbortErrorRetry() $this->assertEquals($message['table'], self::TEST_TABLE_NAME); $this->assertEquals($message['columns'], $cols); return $message['transaction'] - == ['begin' => ['readWrite' => ['readLockMode' => 0], 'excludeTxnFromChangeStreams' => false]] + == [ + 'begin' => [ + 'readWrite' => [ + 'readLockMode' => 0, + 'multiplexedSessionPreviousTransactionId' => '' + ], + 'excludeTxnFromChangeStreams' => false + ] + ] && $message['requestOptions']['transactionTag'] == self::TRANSACTION_TAG; }), Argument::type('array') @@ -2288,36 +2303,26 @@ public function testRunTransactionWithRollback() public function testRunTransactionWithExcludeTxnFromChangeStreams() { - $gapic = $this->prophesize(SpannerClient::class); - - $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $session = new SessionProto(['name' => $sessName]); - $resultSet = new ResultSet(['stats' => new ResultSetStats(['row_count_exact' => 0])]); - $gapic->createSession(Argument::cetera())->shouldBeCalled()->willReturn($session); - $gapic->deleteSession(Argument::cetera())->shouldBeCalled(); - $sql = 'SELECT example FROM sql_query'; $stream = $this->prophesize(ServerStream::class); - $stream->readAll()->shouldBeCalledOnce()->willReturn([$resultSet]); - $gapic->executeStreamingSql($sessName, $sql, Argument::that(function (array $options) { - $this->assertArrayHasKey('transaction', $options); - $this->assertNotNull($transactionOptions = $options['transaction']->getBegin()); - $this->assertTrue($transactionOptions->getExcludeTxnFromChangeStreams()); - return true; - })) + $stream->readAll() ->shouldBeCalledOnce() - ->willReturn($stream->reveal()); + ->willReturn([ + new ResultSet(['stats' => new ResultSetStats(['row_count_exact' => 0])]) + ]); - $database = new Database( - new Grpc(['gapicSpannerClient' => $gapic->reveal()]), - $this->instance, - $this->lro->reveal(), - $this->lroCallables, - self::PROJECT, - self::DATABASE - ); + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) { + $this->assertNotNull($transactionOptions = $request->getTransaction()->getBegin()); + $this->assertTrue($transactionOptions->getExcludeTxnFromChangeStreams()); + return true; + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($stream->reveal()); - $database->runTransaction( + $this->database->runTransaction( function (Transaction $t) use ($sql) { // Run a fake query $t->executeUpdate($sql); @@ -2333,25 +2338,25 @@ function (Transaction $t) use ($sql) { public function testExecutePartitionedUpdateWithExcludeTxnFromChangeStreams() { - $gapic = $this->prophesize(SpannerClient::class); - - $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $session = new SessionProto(['name' => $sessName]); - $gapic->createSession(Argument::cetera())->shouldBeCalled()->willReturn($session); - $gapic->deleteSession(Argument::cetera())->shouldBeCalled(); - $sql = 'SELECT example FROM sql_query'; - $resultSet = new ResultSet(['stats' => new ResultSetStats(['row_count_lower_bound' => 0])]); + $stream = $this->prophesize(ServerStream::class); - $stream->readAll()->shouldBeCalledOnce()->willReturn([$resultSet]); - $gapic->executeStreamingSql($sessName, $sql, Argument::type('array')) + $stream->readAll() + ->shouldBeCalledOnce() + ->willReturn([ + new ResultSet(['stats' => new ResultSetStats(['row_count_lower_bound' => 0])]) + ]); + + $this->spannerClient->executeStreamingSql( + Argument::type(ExecuteSqlRequest::class), + Argument::type('array') + ) ->shouldBeCalledOnce() ->willReturn($stream->reveal()); - $gapic->beginTransaction( - $sessName, - Argument::that(function (TransactionOptions $options) { - $this->assertTrue($options->getExcludeTxnFromChangeStreams()); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertTrue($request->getOptions()->getExcludeTxnFromChangeStreams()); return true; }), Argument::type('array') @@ -2359,16 +2364,7 @@ public function testExecutePartitionedUpdateWithExcludeTxnFromChangeStreams() ->shouldBeCalledOnce() ->willReturn(new TransactionProto(['id' => 'foo'])); - $database = new Database( - new Grpc(['gapicSpannerClient' => $gapic->reveal()]), - $this->instance, - $this->lro->reveal(), - $this->lroCallables, - self::PROJECT, - self::DATABASE - ); - - $database->executePartitionedUpdate( + $this->database->executePartitionedUpdate( $sql, ['transactionOptions' => ['excludeTxnFromChangeStreams' => true]] ); @@ -2376,36 +2372,17 @@ public function testExecutePartitionedUpdateWithExcludeTxnFromChangeStreams() public function testBatchWriteWithExcludeTxnFromChangeStreams() { - $gapic = $this->prophesize(SpannerClient::class); - - $sessName = SpannerClient::sessionName(self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION); - $session = new SessionProto(['name' => $sessName]); - $gapic->createSession(Argument::cetera())->shouldBeCalled()->willReturn($session); - $gapic->deleteSession(Argument::cetera())->shouldBeCalled(); - - $mutationGroups = []; - $gapic->batchWrite( - $sessName, - $mutationGroups, - Argument::that(function ($options) { - $this->assertArrayHasKey('excludeTxnFromChangeStreams', $options); - $this->assertTrue($options['excludeTxnFromChangeStreams']); + $this->spannerClient->batchWrite( + Argument::that(function (BatchWriteRequest $request) { + $this->assertTrue($request->getExcludeTxnFromChangeStreams()); return true; - }) + }), + Argument::type('array') ) ->shouldBeCalledOnce() - ->willReturn(new TransactionProto(['id' => 'foo'])); - - $database = new Database( - new Grpc(['gapicSpannerClient' => $gapic->reveal()]), - $this->instance, - $this->lro->reveal(), - $this->lroCallables, - self::PROJECT, - self::DATABASE - ); + ->willReturn($this->resultGeneratorStream()); - $database->batchWrite($mutationGroups, [ + $this->database->batchWrite([], [ 'excludeTxnFromChangeStreams' => true ]); } @@ -2499,7 +2476,7 @@ private function stubExecuteStreamingSql($transactionOptions = self::BEGIN_RW_OP Argument::that(function (ExecuteSqlRequest $request) use ($sql, $transactionOptions) { return $request->getSql() == $sql && $request->getTransaction() == $this->serializer->decodeMessage( - new TransactionSelector, + new TransactionSelector(), $transactionOptions ) && $request->getRequestOptions()->getTransactionTag() == self::TRANSACTION_TAG; @@ -2520,7 +2497,7 @@ private function stubExecuteBatchDml($transactionOptions = self::BEGIN_RW_OPTION Argument::that(function (ExecuteBatchDmlRequest $request) use ($transactionOptions) { $this->assertEquals( $request->getTransaction(), - $this->serializer->decodeMessage(new TransactionSelector, $transactionOptions) + $this->serializer->decodeMessage(new TransactionSelector(), $transactionOptions) ); $this->assertEquals( $request->getRequestOptions()->getTransactionTag(), diff --git a/Spanner/tests/Unit/DateTest.php b/Spanner/tests/Unit/DateTest.php index 1e16f5c8360a..ee4a3d9a32cf 100644 --- a/Spanner/tests/Unit/DateTest.php +++ b/Spanner/tests/Unit/DateTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Spanner\Date; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\Date; use PHPUnit\Framework\TestCase; /** @@ -57,7 +57,7 @@ public function testFormatAsString() public function testCast() { - $this->assertEquals($this->dt->format(Date::FORMAT), (string)$this->date); + $this->assertEquals($this->dt->format(Date::FORMAT), (string) $this->date); } public function testType() diff --git a/Spanner/tests/Unit/InstanceConfigurationTest.php b/Spanner/tests/Unit/InstanceConfigurationTest.php index 8ec031e493a0..7ae740db6fed 100644 --- a/Spanner/tests/Unit/InstanceConfigurationTest.php +++ b/Spanner/tests/Unit/InstanceConfigurationTest.php @@ -17,15 +17,14 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\ApiCore\OperationResponse; use Google\ApiCore\ApiException; +use Google\ApiCore\OperationResponse; use Google\ApiCore\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; use Google\Cloud\Spanner\InstanceConfiguration; use Google\LongRunning\Operation; @@ -137,7 +136,7 @@ public function testExistsDoesntExist() Argument::type('array') ) ->shouldBeCalledOnce() - ->will(function() { + ->will(function () { throw new ApiException('', Code::NOT_FOUND); }); diff --git a/Spanner/tests/Unit/InstanceTest.php b/Spanner/tests/Unit/InstanceTest.php index d821ee9d04bf..bd48de91ab62 100644 --- a/Spanner/tests/Unit/InstanceTest.php +++ b/Spanner/tests/Unit/InstanceTest.php @@ -17,42 +17,41 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\ApiCore\Serializer; use Google\ApiCore\OperationResponse; -use Google\ApiCore\PagedListResponse; use Google\ApiCore\Page; -use Google\LongRunning\Operation; +use Google\ApiCore\PagedListResponse; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; -use Google\LongRunning\Client\OperationsClient; use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\Database as DatabaseProto; -use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse; use Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsResponse; -use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesResponse; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsResponse; use Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsResponse; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesResponse; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; +use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; +use Google\Cloud\Spanner\KeySet; +use Google\Cloud\Spanner\Result; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; use Google\Cloud\Spanner\V1\Client\SpannerClient; -use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type; -use Google\Cloud\Spanner\V1\Session; use Google\Cloud\Spanner\V1\CreateSessionRequest; use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type; use Google\Cloud\Spanner\V1\ExecuteSqlRequest; -use Google\Cloud\Spanner\Backup; +use Google\Cloud\Spanner\V1\Session; +use Google\LongRunning\Client\OperationsClient; +use Google\LongRunning\Operation; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Spanner\Result; -use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; /** * @group spanner @@ -143,7 +142,7 @@ public function testInfo() public function testInfoWithReloadAndFieldMask() { - $requestedFieldNames = ["name", 'node_count']; + $requestedFieldNames = ['name', 'node_count']; $this->instanceAdminClient->getInstance( Argument::that(function ($request) use ($requestedFieldNames) { $message = $this->serializer->encodeMessage($request); @@ -233,7 +232,7 @@ public function testReload() public function testReloadWithFieldMask() { - $requestedFieldNames = ["name", 'node_count']; + $requestedFieldNames = ['name', 'node_count']; $this->instanceAdminClient->getInstance( Argument::that(function ($request) use ($requestedFieldNames) { $message = $this->serializer->encodeMessage($request); @@ -501,8 +500,8 @@ public function testDatabasesPaged() $page1 = $this->prophesize(Page::class); $page1->getResponseObject() ->willReturn(new ListDatabasesResponse([ - 'databases' => [$databases[0]], 'next_page_token' => 'foo'] - )); + 'databases' => [$databases[0]], 'next_page_token' => 'foo' + ])); $pagedListResponse1 = $this->prophesize(PagedListResponse::class); $pagedListResponse1->getPage() ->willReturn($page1->reveal()); diff --git a/Spanner/tests/Unit/KeyRangeTest.php b/Spanner/tests/Unit/KeyRangeTest.php index a4da57680852..0082a040de16 100644 --- a/Spanner/tests/Unit/KeyRangeTest.php +++ b/Spanner/tests/Unit/KeyRangeTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\KeyRange; use InvalidArgumentException; use PHPUnit\Framework\TestCase; @@ -35,7 +35,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->range = new KeyRange; + $this->range = new KeyRange(); } public function testConstructWithScalars() diff --git a/Spanner/tests/Unit/KeySetTest.php b/Spanner/tests/Unit/KeySetTest.php index 8b2c772b7840..05f68e89b5bd 100644 --- a/Spanner/tests/Unit/KeySetTest.php +++ b/Spanner/tests/Unit/KeySetTest.php @@ -32,7 +32,7 @@ class KeySetTest extends TestCase public function testAddRange() { - $set = new KeySet; + $set = new KeySet(); $range = $this->prophesize(KeyRange::class); $range->keyRangeObject()->willReturn('foo'); @@ -43,7 +43,7 @@ public function testAddRange() public function testSetRanges() { - $set = new KeySet; + $set = new KeySet(); $range1 = $this->prophesize(KeyRange::class); $range1->keyRangeObject()->willReturn('foo'); @@ -64,7 +64,7 @@ public function testSetRanges() public function testAddKey() { - $set = new KeySet; + $set = new KeySet(); $key = 'key'; @@ -75,9 +75,9 @@ public function testAddKey() public function testSetKeys() { - $set = new KeySet; + $set = new KeySet(); - $keys = ['key1','key2']; + $keys = ['key1', 'key2']; $set->setKeys($keys); @@ -86,7 +86,7 @@ public function testSetKeys() public function testSetMatchAll() { - $set = new KeySet; + $set = new KeySet(); $set->setMatchAll(true); $this->assertTrue($set->keySetObject()['all']); @@ -97,7 +97,7 @@ public function testSetMatchAll() public function testRanges() { - $set = new KeySet; + $set = new KeySet(); $range = $this->prophesize(KeyRange::class)->reveal(); $set->addRange($range); @@ -106,7 +106,7 @@ public function testRanges() public function testKeys() { - $set = new KeySet; + $set = new KeySet(); $key = 'foo'; $set->addKey($key); @@ -139,7 +139,7 @@ public function testInvalidAll() public function testFromArray() { $range = new KeyRange(['start' => 'foo', 'end' => 'bar']); - $keys = ['a','b']; + $keys = ['a', 'b']; $all = true; $res = (new KeySet([ 'keys' => $keys, diff --git a/Spanner/tests/Unit/OperationTest.php b/Spanner/tests/Unit/OperationTest.php index d4c932eaf47b..2a4a85acaadd 100644 --- a/Spanner/tests/Unit/OperationTest.php +++ b/Spanner/tests/Unit/OperationTest.php @@ -21,10 +21,8 @@ use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; -use Google\Cloud\Spanner\Connection\Grpc; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; @@ -35,26 +33,27 @@ use Google\Cloud\Spanner\Snapshot; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Spanner\V1\BeginTransactionRequest; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\CommitResponse; use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; +use Google\Cloud\Spanner\V1\ExecuteSqlRequest; use Google\Cloud\Spanner\V1\PartialResultSet; use Google\Cloud\Spanner\V1\Partition; use Google\Cloud\Spanner\V1\PartitionResponse; +use Google\Cloud\Spanner\V1\ResultSet; use Google\Cloud\Spanner\V1\ResultSetMetadata; +use Google\Cloud\Spanner\V1\ResultSetStats; use Google\Cloud\Spanner\V1\StructType; use Google\Cloud\Spanner\V1\StructType\Field; use Google\Cloud\Spanner\V1\Transaction as TransactionProto; use Google\Cloud\Spanner\V1\Type; -use Google\Protobuf\Value; use Google\Protobuf\Duration; use Google\Protobuf\Timestamp as TimestampProto; -use Google\Cloud\Spanner\V1\ResultSet; -use Google\Cloud\Spanner\V1\ResultSetStats; -use Google\Cloud\Spanner\V1\TransactionOptions; +use Google\Protobuf\Value; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; /** * @group spanner @@ -414,11 +413,9 @@ public function testTransactionNoTag() public function testTransactionWithExcludeTxnFromChangeStreams() { - $gapic = $this->prophesize(SpannerClient::class); - $gapic->beginTransaction( - self::SESSION, - Argument::that(function (TransactionOptions $options) { - $this->assertTrue($options->getExcludeTxnFromChangeStreams()); + $this->spannerClient->beginTransaction( + Argument::that(function (BeginTransactionRequest $request) { + $this->assertTrue($request->getOptions()->getExcludeTxnFromChangeStreams()); return true; }), Argument::type('array') @@ -426,12 +423,7 @@ public function testTransactionWithExcludeTxnFromChangeStreams() ->shouldBeCalled() ->willReturn(new TransactionProto(['id' => 'foo'])); - $operation = new Operation( - new Grpc(['gapicSpannerClient' => $gapic->reveal()]), - true - ); - - $transaction = $operation->transaction($this->session, [ + $transaction = $this->operation->transaction($this->session, [ 'transactionOptions' => ['excludeTxnFromChangeStreams' => true] ]); @@ -446,28 +438,23 @@ public function testExecuteAndExecuteUpdateWithExcludeTxnFromChangeStreams() $stream = $this->prophesize(ServerStream::class); $stream->readAll()->shouldBeCalledTimes(2)->willReturn([$resultSet]); - $gapic = $this->prophesize(SpannerClient::class); - $gapic->executeStreamingSql(self::SESSION, $sql, Argument::that(function (array $options) { - $this->assertArrayHasKey('transaction', $options); - $this->assertNotNull($transactionOptions = $options['transaction']->getBegin()); - $this->assertTrue($transactionOptions->getExcludeTxnFromChangeStreams()); - return true; - })) + $this->spannerClient->executeStreamingSql( + Argument::that(function (ExecuteSqlRequest $request) { + $this->assertTrue($request->getTransaction()->getBegin()->getExcludeTxnFromChangeStreams()); + return true; + }), + Argument::type('array') + ) ->shouldBeCalledTimes(2) ->willReturn($stream->reveal()); - $operation = new Operation( - new Grpc(['gapicSpannerClient' => $gapic->reveal()]), - true - ); - - $operation->execute($this->session, $sql, [ + $this->operation->execute($this->session, $sql, [ 'transaction' => ['begin' => ['excludeTxnFromChangeStreams' => true]] ]); $transaction = $this->prophesize(Transaction::class)->reveal(); - $operation->executeUpdate($this->session, $transaction, $sql, [ + $this->operation->executeUpdate($this->session, $transaction, $sql, [ 'transaction' => ['begin' => ['excludeTxnFromChangeStreams' => true]] ]); } diff --git a/Spanner/tests/Unit/PgJsonbTest.php b/Spanner/tests/Unit/PgJsonbTest.php index 59bc844aabd0..0450f6be6384 100644 --- a/Spanner/tests/Unit/PgJsonbTest.php +++ b/Spanner/tests/Unit/PgJsonbTest.php @@ -46,7 +46,7 @@ public function validValueProvider() { $obj = $this->prophesize('stdClass'); $obj->willImplement('JsonSerializable'); - $obj->jsonSerialize()->willReturn(["a" => 1, "b" => null]); + $obj->jsonSerialize()->willReturn(['a' => 1, 'b' => null]); return [ @@ -56,7 +56,7 @@ public function validValueProvider() // // null value shouldn't be casted [null, null], // // arrays should be converted to JSON - [["a"=>1.1, "b"=>"2"], '{"a":1.1,"b":"2"}'], + [['a' => 1.1, 'b' => '2'], '{"a":1.1,"b":"2"}'], // JsonSerializable should be used after a json_encode call [$obj->reveal(), '{"a":1,"b":null}'] ]; diff --git a/Spanner/tests/Unit/PgNumericTest.php b/Spanner/tests/Unit/PgNumericTest.php index 71cbd3d9542f..38671301fae5 100644 --- a/Spanner/tests/Unit/PgNumericTest.php +++ b/Spanner/tests/Unit/PgNumericTest.php @@ -18,8 +18,8 @@ namespace Google\Cloud\Spanner\Tests\Unit; use Google\Cloud\Spanner\PgNumeric; -use Google\Cloud\Spanner\V1\TypeCode; use Google\Cloud\Spanner\V1\TypeAnnotationCode; +use Google\Cloud\Spanner\V1\TypeCode; use PHPUnit\Framework\TestCase; /** diff --git a/Spanner/tests/Unit/ResultTest.php b/Spanner/tests/Unit/ResultTest.php index 89aa0785811a..95232b932aa6 100644 --- a/Spanner/tests/Unit/ResultTest.php +++ b/Spanner/tests/Unit/ResultTest.php @@ -18,17 +18,17 @@ namespace Google\Cloud\Spanner\Tests\Unit; use Google\Cloud\Core\Exception\ServiceException; -use Google\Cloud\Spanner\Transaction; +use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\Operation; +use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Snapshot; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; +use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\ValueMapper; -use Google\Cloud\Spanner\Operation; -use Google\Cloud\Spanner\Result; -use Google\Cloud\Core\Testing\GrpcTestTrait; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; /** * @group spanner @@ -54,6 +54,7 @@ class ResultTest extends TestCase private $session; private $transaction; private $snapshot; + private $mapper; public function setUp(): void { diff --git a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php index 7dab5dcb57f9..3ca6a4c88564 100644 --- a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php +++ b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php @@ -18,20 +18,20 @@ namespace Google\Cloud\Spanner\Tests\Unit\Session; use Google\Auth\Cache\MemoryCacheItemPool; +use Google\Cloud\Core\RequestHandler; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Lock\MockValues; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Session\CacheSessionPool; use Google\Cloud\Spanner\Session\Session; -use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\RequestHandler; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\RejectedPromise; -use Psr\Cache\CacheItemInterface; -use Psr\Cache\CacheItemPoolInterface; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Argument\ArgumentsWildcard; use Prophecy\PhpUnit\ProphecyTrait; +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; use ReflectionMethod; /** @@ -84,7 +84,7 @@ public function badConfigDataProvider() [['maxCyclesToWaitForSession' => -1]], [['sleepIntervalSeconds' => -1]], [['minSessions' => 5, 'maxSessions' => 1]], - [['lock' => new \stdClass]] + [['lock' => new \stdClass()]] ]; } @@ -894,11 +894,11 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f $database->name() ->willReturn(self::DATABASE_NAME); $database->execute(Argument::exact('SELECT 1'), Argument::withKey('session')) - ->willReturn(new DumbObject); + ->willReturn(new DumbObject()); $createRes = function ($args, $mock, $method) use ($shouldCreateFails) { if ($shouldCreateFails) { - throw new \Exception("error"); + throw new \Exception('error'); } $methodCalls = $mock->findProphecyMethodCalls( @@ -1049,7 +1049,7 @@ public function testMaintainServerDeletedSessions( $data = [] ) { $cacheData = $this->cacheData($initialItems, $maintainInterval); - $expiredTime = $this->time - 28*24*60*60; // 28 days + $expiredTime = $this->time - 28 * 24 * 60 * 60; // 28 days foreach ($cacheData['queue'] as $k => $v) { $cacheData['queue'][$k]['creation'] = $expiredTime; } @@ -1191,7 +1191,7 @@ public function testSessionPoolDatabaseRole() $database->batchCreateSessions([ 'sessionTemplate' => ['labels' => [], 'creator_role' => 'Reader'], 'sessionCount' => 1]) ->shouldBeCalled() - ->willReturn(['session' => array(['name' => 'session', 'expirtation' => $this->time])]); + ->willReturn(['session' => [['name' => 'session', 'expirtation' => $this->time]]]); $pool->setDatabase($database->reveal()); $pool->warmup(); diff --git a/Spanner/tests/Unit/SnapshotTest.php b/Spanner/tests/Unit/SnapshotTest.php index 04194e5b0c98..e88849a8e50a 100644 --- a/Spanner/tests/Unit/SnapshotTest.php +++ b/Spanner/tests/Unit/SnapshotTest.php @@ -17,17 +17,17 @@ namespace Google\Cloud\Spanner\Tests\Unit; +use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Operation; +use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Snapshot; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Core\Testing\GrpcTestTrait; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Cloud\Spanner\KeySet; -use Google\Cloud\Spanner\Result; /** * @group spanner @@ -45,7 +45,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->timestamp = new Timestamp(new \DateTime); + $this->timestamp = new Timestamp(new \DateTime()); $args = [ 'id' => 'foo', diff --git a/Spanner/tests/Unit/SpannerClientTest.php b/Spanner/tests/Unit/SpannerClientTest.php index fce9eb805e42..526d3cc6da18 100644 --- a/Spanner/tests/Unit/SpannerClientTest.php +++ b/Spanner/tests/Unit/SpannerClientTest.php @@ -18,20 +18,19 @@ namespace Google\Cloud\Spanner\Tests\Unit; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; -use Google\ApiCore\PagedListResponse; use Google\ApiCore\Page; +use Google\ApiCore\PagedListResponse; +use Google\ApiCore\Serializer; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\Fixtures; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsResponse; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesResponse; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; -use Google\Cloud\Spanner\Admin\Instance\V1\Instance as InstanceProto; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\CommitTimestamp; @@ -39,12 +38,12 @@ use Google\Cloud\Spanner\Date; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\InstanceConfiguration; -use Google\Cloud\Spanner\PgJsonb; -use Google\Cloud\Spanner\PgOid; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Numeric; +use Google\Cloud\Spanner\PgJsonb; use Google\Cloud\Spanner\PgNumeric; +use Google\Cloud\Spanner\PgOid; use Google\Cloud\Spanner\SpannerClient; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; @@ -73,7 +72,6 @@ class SpannerClientTest extends TestCase private $directedReadOptionsIncludeReplicas; private $operationResponse; - public function setUp(): void { $this->checkAndSkipGrpcTests(); @@ -464,18 +462,18 @@ public function testBytes() { $b = $this->spannerClient->bytes('foo'); $this->assertInstanceOf(Bytes::class, $b); - $this->assertEquals(base64_encode('foo'), (string)$b); + $this->assertEquals(base64_encode('foo'), (string) $b); } public function testDate() { - $d = $this->spannerClient->date(new \DateTime); + $d = $this->spannerClient->date(new \DateTime()); $this->assertInstanceOf(Date::class, $d); } public function testTimestamp() { - $ts = $this->spannerClient->timestamp(new \DateTime); + $ts = $this->spannerClient->timestamp(new \DateTime()); $this->assertInstanceOf(Timestamp::class, $ts); } @@ -499,12 +497,12 @@ public function testPgJsonB() $strVal = $this->spannerClient->pgJsonb('{}'); $this->assertInstanceOf(PgJsonb::class, $strVal); - $arrVal = $this->spannerClient->pgJsonb(["a" => 1, "b" => 2]); + $arrVal = $this->spannerClient->pgJsonb(['a' => 1, 'b' => 2]); $this->assertInstanceOf(PgJsonb::class, $arrVal); $stub = $this->prophesize('stdClass'); $stub->willImplement('JsonSerializable'); - $stub->jsonSerialize()->willReturn(["a" => 1, "b" => null]); + $stub->jsonSerialize()->willReturn(['a' => 1, 'b' => null]); $objVal = $this->spannerClient->pgJsonb($stub->reveal()); $this->assertInstanceOf(PgJsonb::class, $objVal); } diff --git a/Spanner/tests/Unit/StructTypeTest.php b/Spanner/tests/Unit/StructTypeTest.php index 465bbbda925e..d624e4db6679 100644 --- a/Spanner/tests/Unit/StructTypeTest.php +++ b/Spanner/tests/Unit/StructTypeTest.php @@ -54,7 +54,7 @@ public function testEnqueueInConstructor() public function testChainableAdd() { - $type = new StructType; + $type = new StructType(); $type->add($this->definition[0]['name'], $this->definition[0]['type']) ->add($this->definition[1]['name'], $this->definition[1]['child']); @@ -64,7 +64,7 @@ public function testChainableAdd() public function testAddUnnamed() { - $type = new StructType; + $type = new StructType(); $type->addUnnamed(Database::TYPE_STRING); $this->assertEquals($type->fields(), [ [ @@ -80,7 +80,7 @@ public function testAddInvalidType() $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Field type `foo` is not valid.'); - (new StructType)->add('name', 'foo'); + (new StructType())->add('name', 'foo'); } /** @@ -90,7 +90,7 @@ public function testInvalidTypeDefinition($type) { $this->expectException(InvalidArgumentException::class); - (new StructType)->add('foo', $type); + (new StructType())->add('foo', $type); } public function definitionTypes() @@ -103,8 +103,8 @@ public function definitionTypes() public function testAddChildStruct() { - $str = new StructType; - $str->add('foo', new StructType); + $str = new StructType(); + $str->add('foo', new StructType()); $fields = $str->fields(); $this->assertEquals(Database::TYPE_STRUCT, $fields[0]['type']); @@ -113,7 +113,7 @@ public function testAddChildStruct() public function testAddChildArray() { - $str = new StructType; + $str = new StructType(); $str->add('foo', new ArrayType(null)); $fields = $str->fields(); diff --git a/Spanner/tests/Unit/StructValueTest.php b/Spanner/tests/Unit/StructValueTest.php index ab79505030ab..3fa728252f0a 100644 --- a/Spanner/tests/Unit/StructValueTest.php +++ b/Spanner/tests/Unit/StructValueTest.php @@ -50,7 +50,7 @@ public function testConstructor() public function testAdd() { - $val = new StructValue; + $val = new StructValue(); $val->add($this->values[0]['name'], $this->values[0]['value']) ->add($this->values[1]['name'], $this->values[1]['value']); @@ -59,7 +59,7 @@ public function testAdd() public function testAddUnnamed() { - $val = new StructValue; + $val = new StructValue(); $val->addUnnamed($this->values[0]['value']) ->addUnnamed($this->values[1]['value']); diff --git a/Spanner/tests/Unit/TimestampTest.php b/Spanner/tests/Unit/TimestampTest.php index 10fbbb0a764c..083d6ebbbf1a 100644 --- a/Spanner/tests/Unit/TimestampTest.php +++ b/Spanner/tests/Unit/TimestampTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Core\Testing\GrpcTestTrait; +use Google\Cloud\Spanner\Timestamp; use PHPUnit\Framework\TestCase; /** @@ -56,7 +56,7 @@ public function testCast() { $this->assertEquals( (new \DateTime($this->dt->format(Timestamp::FORMAT)))->format('U'), - (new \DateTime(str_replace('000000000', '000000', (string)$this->ts)))->format('U') + (new \DateTime(str_replace('000000000', '000000', (string) $this->ts)))->format('U') ); } diff --git a/Spanner/tests/Unit/TransactionConfigurationTraitTest.php b/Spanner/tests/Unit/TransactionConfigurationTraitTest.php index cb3bdf40e651..8d8aaaa51e0a 100644 --- a/Spanner/tests/Unit/TransactionConfigurationTraitTest.php +++ b/Spanner/tests/Unit/TransactionConfigurationTraitTest.php @@ -46,7 +46,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->impl = new TransactionConfigurationTraitImplementation; + $this->impl = new TransactionConfigurationTraitImplementation(); $this->duration = new Duration(['seconds' => 10, 'nanos' => 1]); $this->dur = new Duration(['seconds' => 10, 'nanos' => 1]); $this->directedReadOptionsIncludeReplicas = [ diff --git a/Spanner/tests/Unit/TransactionTest.php b/Spanner/tests/Unit/TransactionTest.php index e58d69b6fe79..145639340447 100644 --- a/Spanner/tests/Unit/TransactionTest.php +++ b/Spanner/tests/Unit/TransactionTest.php @@ -19,8 +19,8 @@ use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; -use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\BatchDmlResult; use Google\Cloud\Spanner\Database; @@ -35,13 +35,10 @@ use Google\Cloud\Spanner\V1\ExecuteBatchDmlRequest; use Google\Cloud\Spanner\V1\ExecuteBatchDmlResponse; use Google\Cloud\Spanner\V1\ExecuteSqlRequest; -use Google\Cloud\Spanner\V1\ResultSet; use Google\Cloud\Spanner\V1\ReadRequest; -use Google\Cloud\Spanner\V1\RollbackRequest; +use Google\Cloud\Spanner\V1\ResultSet; use Google\Cloud\Spanner\V1\ResultSetStats; -use Google\Cloud\Spanner\V1\CommitResponse; -use Google\Cloud\Spanner\V1\CommitResponse\CommitStats; -use Google\Protobuf\Timestamp as TimestampProto; +use Google\Cloud\Spanner\V1\RollbackRequest; use Google\Protobuf\Duration; use Google\Rpc\Status; use InvalidArgumentException; @@ -355,7 +352,7 @@ public function testExecuteUpdateBatchError() ['requestOptions' => ['requestTag' => self::REQUEST_TAG]] ); - $this->assertEquals([1,2], $res->rowCounts()); + $this->assertEquals([1, 2], $res->rowCounts()); $this->assertEquals($err, $res->error()['status']); $this->assertEquals($statements[2], $res->error()['statement']); } @@ -454,7 +451,6 @@ public function testRead() ->shouldBeCalledOnce() ->willReturn($this->resultGeneratorStream()); - $res = $this->transaction->read( $table, new KeySet(['all' => true]), @@ -675,7 +671,6 @@ public function testInvalidReadContext() { $this->expectException(\BadMethodCallException::class); - $singleUseTransaction = new Transaction( $this->operation, $this->session, diff --git a/Spanner/tests/Unit/TransactionTypeTest.php b/Spanner/tests/Unit/TransactionTypeTest.php index c3700ff46e80..725a0b260986 100644 --- a/Spanner/tests/Unit/TransactionTypeTest.php +++ b/Spanner/tests/Unit/TransactionTypeTest.php @@ -21,7 +21,6 @@ use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; -use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; @@ -31,10 +30,10 @@ use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\Snapshot; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\V1\BeginTransactionRequest; -use Google\Cloud\Spanner\V1\BeginTransactionResponse; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\CommitRequest; use Google\Cloud\Spanner\V1\CommitResponse; @@ -47,15 +46,13 @@ use Google\Cloud\Spanner\V1\Session; use Google\Cloud\Spanner\V1\Transaction as TransactionProto; use Google\Cloud\Spanner\V1\TransactionOptions; -use Google\Cloud\Spanner\V1\TransactionSelector; use Google\Cloud\Spanner\V1\TransactionOptions\PBReadOnly; use Google\Cloud\Spanner\V1\TransactionOptions\ReadWrite; use Google\Protobuf\Duration; +use Google\Protobuf\Timestamp as TimestampProto; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Google\Protobuf\Timestamp as TimestampProto; -use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; /** * @group spanner @@ -406,7 +403,7 @@ public function testDatabaseSnapshotPreAllocateReadTimestampAndExactStaleness($c $this->serializer->decodeMessage( Argument::type(BeginTransactionRequest::class), - Argument::that(function (array $data) use ($options){ + Argument::that(function (array $data) use ($options) { $this->assertEquals($data['options'], $options); return true; }), @@ -489,7 +486,7 @@ public function testDatabasePreAllocatedSnapshotStrongConsistency($chunks) $this->serializer->decodeMessage( Argument::type(BeginTransactionRequest::class), - Argument::that(function (array $data) use ($options){ + Argument::that(function (array $data) use ($options) { $this->assertEquals($data['options'], $options); return true; }), @@ -568,7 +565,7 @@ public function testDatabasePreAllocatedSnapshotDefaultsToStrongConsistency($chu $this->serializer->decodeMessage( Argument::type(BeginTransactionRequest::class), - Argument::that(function (array $data) use ($options){ + Argument::that(function (array $data) use ($options) { $this->assertEquals($data['options'], $options); return true; }), @@ -615,7 +612,7 @@ public function testDatabaseSnapshotReturnReadTimestamp($chunks) $this->serializer->decodeMessage( Argument::type(BeginTransactionRequest::class), - Argument::that(function (array $data) use ($options){ + Argument::that(function (array $data) use ($options) { $this->assertEquals($data['options'], $options); return true; }), @@ -726,7 +723,7 @@ public function testDatabaseDeleteSingleUseReadWrite() { $database = $this->createMockedCommitDatabase(); - $database->delete('Table', new KeySet); + $database->delete('Table', new KeySet()); } /** @@ -831,7 +828,7 @@ public function testDatabaseReadSingleUseReadOnly($chunks) $serializer = $this->serializerForStreamingRead($chunks, $transaction); $database = $this->database($this->spannerClient->reveal(), $serializer); - $database->read('Table', new KeySet, [])->rows()->current(); + $database->read('Table', new KeySet(), [])->rows()->current(); } /** @@ -857,7 +854,7 @@ public function testDatabaseReadBeginReadOnly($chunks) $serializer = $this->serializerForStreamingRead($chunks, $transaction); $database = $this->database($this->spannerClient->reveal(), $serializer); - $database->read('Table', new KeySet, [], [ + $database->read('Table', new KeySet(), [], [ 'begin' => true ])->rows()->current(); } @@ -882,7 +879,7 @@ public function testDatabaseReadBeginReadWrite($chunks) $serializer = $this->serializerForStreamingRead($chunks, $transaction); $database = $this->database($this->spannerClient->reveal(), $serializer); - $database->read('Table', new KeySet, [], [ + $database->read('Table', new KeySet(), [], [ 'begin' => true, 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE ])->rows()->current(); @@ -980,7 +977,7 @@ private function serializerForStreamingSql(array $chunks, array $expectedTransac { // mock serializer responses for streaming read $this->serializer->decodeMessage( - Argument::type(ExecuteSqlRequest::class), + Argument::type(ExecuteSqlRequest::class), Argument::that(function ($data) use ($expectedTransaction) { $this->assertEquals($data['transaction'], $expectedTransaction); return true; @@ -1013,7 +1010,7 @@ private function getFullyQualifiedSessionName() private function createTransactionOptions($options = []) { $serializer = new Serializer(); - $transactionOptions = new TransactionOptions; + $transactionOptions = new TransactionOptions(); if (isset($options['readOnly'])) { $readOnly = $serializer->decodeMessage( new PBReadOnly(), diff --git a/Spanner/tests/Unit/V1/Client/SpannerClientTest.php b/Spanner/tests/Unit/V1/Client/SpannerClientTest.php index 7fadce9cf97d..f013ee470349 100644 --- a/Spanner/tests/Unit/V1/Client/SpannerClientTest.php +++ b/Spanner/tests/Unit/V1/Client/SpannerClientTest.php @@ -24,7 +24,6 @@ use Google\ApiCore\ApiException; use Google\ApiCore\CredentialsWrapper; -use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; use Google\ApiCore\Testing\GeneratedTest; use Google\ApiCore\Testing\MockTransport; diff --git a/Spanner/tests/Unit/ValueMapperTest.php b/Spanner/tests/Unit/ValueMapperTest.php index 9b9780f3a2a0..5e36b057dad7 100644 --- a/Spanner/tests/Unit/ValueMapperTest.php +++ b/Spanner/tests/Unit/ValueMapperTest.php @@ -29,9 +29,9 @@ use Google\Cloud\Spanner\StructType; use Google\Cloud\Spanner\StructValue; use Google\Cloud\Spanner\Timestamp; -use Google\Cloud\Spanner\ValueMapper; use Google\Cloud\Spanner\V1\TypeAnnotationCode; use Google\Cloud\Spanner\V1\TypeCode; +use Google\Cloud\Spanner\ValueMapper; use PHPUnit\Framework\TestCase; /** @@ -253,7 +253,7 @@ public function testFormatParamsForExecuteSqlArrayTypeNestedStruct() ]; $types = [ - 'foo' => new ArrayType((new StructType)->add('hello', Database::TYPE_STRING)) + 'foo' => new ArrayType((new StructType())->add('hello', Database::TYPE_STRING)) ]; $res = $this->mapper->formatParamsForExecuteSql($params, $types); @@ -314,7 +314,7 @@ public function testFormatParamsForExecuteSqlArrayMismatchedDefinition() $this->expectExceptionMessage('Array data does not match given array parameter type.'); $params = [ - 'foo' => [1,2,3] + 'foo' => [1, 2, 3] ]; $types = [ @@ -327,7 +327,7 @@ public function testFormatParamsForExecuteSqlArrayMismatchedDefinition() public function testFormatParamsForExecuteSqlArrayForCustomTypes() { $params = [ - 'foo' => [1,2,3] + 'foo' => [1, 2, 3] ]; $types = [ @@ -396,7 +396,7 @@ public function testFormatParamsForExecuteSqlStruct() ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add('name', Database::TYPE_STRING) ->add('age', Database::TYPE_INT64) ->add('jobs', new ArrayType(Database::TYPE_STRING)) @@ -484,7 +484,7 @@ public function testFormatParamsForExecuteSqlInvalidStructValue() ]; $types = [ - 'foo' => new StructType + 'foo' => new StructType() ]; $this->mapper->formatParamsForExecuteSql($params, $types); @@ -493,14 +493,14 @@ public function testFormatParamsForExecuteSqlInvalidStructValue() public function testFormatParamsForExecuteSqlStructDuplicateFieldNames() { $params = [ - 'foo' => (new StructValue) + 'foo' => (new StructValue()) ->add('hello', 'world') ->add('hello', 10) ->add('hello', 'goodbye') ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add('hello', Database::TYPE_STRING) ->add('hello', Database::TYPE_INT64) ->add('hello', Database::TYPE_STRING) @@ -543,7 +543,7 @@ public function testFormatParamsForExecuteSqlStructDuplicateFieldNames() public function testFormatParamsForExecuteSqlStructUnnamedFields() { $params = [ - 'foo' => (new StructValue) + 'foo' => (new StructValue()) ->addUnnamed('hello') ->addUnnamed(10) ->add('key', 'val') @@ -551,7 +551,7 @@ public function testFormatParamsForExecuteSqlStructUnnamedFields() ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add(null, Database::TYPE_STRING) ->addUnnamed(Database::TYPE_INT64) ->add('key', Database::TYPE_STRING) @@ -607,7 +607,7 @@ public function testFormatParamsForExecuteSqlInferredStructValueType() ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add('hello', Database::TYPE_STRING) ->add('num', Database::TYPE_INT64) ]; @@ -643,14 +643,14 @@ public function testFormatParamsForExecuteSqlInferredStructValueType() public function testFormatParamsForExecuteSqlInferredStructValueTypeWithUnnamed() { $params = [ - 'foo' => (new StructValue) + 'foo' => (new StructValue()) ->add('hello', 'world') ->addUnnamed('foo') ->add('num', 10) ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add('hello', Database::TYPE_STRING) ->add('num', Database::TYPE_INT64) ]; @@ -683,7 +683,7 @@ public function testFormatParamsForExecuteSqlStdClassValue() ]; $types = [ - 'foo' => (new StructType) + 'foo' => (new StructType()) ->add('hello', Database::TYPE_STRING) ]; @@ -921,7 +921,7 @@ public function testDecodeValuesString() public function testDecodeValuesTimestamp() { - $dt = new \DateTime; + $dt = new \DateTime(); $str = $dt->format(Timestamp::FORMAT); $res = $this->mapper->decodeValues( @@ -936,7 +936,7 @@ public function testDecodeValuesTimestamp() public function testDecodeValuesDate() { - $dt = new \DateTime; + $dt = new \DateTime(); $res = $this->mapper->decodeValues( $this->createField(Database::TYPE_DATE), $this->createRow($dt->format(Date::FORMAT)), From b640911dc8b4217c0acfa43552321fd8a0527830 Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Tue, 12 Nov 2024 20:04:23 +0000 Subject: [PATCH 3/8] test fixes --- Spanner/src/Backup.php | 2 +- Spanner/src/Database.php | 4 ++-- Spanner/src/FormatKeySetTrait.php | 3 ++- Spanner/src/SpannerClient.php | 21 ++++++++++++++++----- Spanner/tests/System/AdminTest.php | 3 ++- Spanner/tests/System/PgWriteTest.php | 3 ++- Spanner/tests/System/WriteTest.php | 3 ++- Spanner/tests/Unit/BackupTest.php | 4 +++- dev/composer.json | 1 + 9 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index 44e300a271e1..14f404aa2d04 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -71,7 +71,7 @@ public function __construct( private DatabaseAdminClient $databaseAdminClient, private Serializer $serializer, private Instance $instance, - private $projectId, + private string $projectId, private string $name, private array $info = [] ) { diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index d0c9df578a4e..a19fe541ed99 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -2601,9 +2601,9 @@ private function fieldValue($param) private function databaseResultFunction(): Closure { - return function (DatabaseProto $database) { + return function (DatabaseProto $database): self { $name = DatabaseAdminClient::parseName($database->getName()); - return $this->instance->database($name['name'], [ + return $this->instance->database($name['database'], [ 'sessionPool' => $this->sessionPool, 'database' => $this->serializer->encodeMessage($database), 'databaseRole' => $this->databaseRole, diff --git a/Spanner/src/FormatKeySetTrait.php b/Spanner/src/FormatKeySetTrait.php index cc1f1ef4b9b6..4607bd5d12d8 100644 --- a/Spanner/src/FormatKeySetTrait.php +++ b/Spanner/src/FormatKeySetTrait.php @@ -34,4 +34,5 @@ private function formatKeySet(array $keySet) return $keySet; } -} \ No newline at end of file +} + diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index e56e9d8322e6..82c4072ef44b 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -26,6 +26,7 @@ use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\ClientTrait; use Google\Cloud\Core\Exception\GoogleException; +use Google\Cloud\Core\EmulatorTrait; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; @@ -108,6 +109,7 @@ class SpannerClient use ApiHelperTrait; use ClientOptionsTrait; use ClientTrait; + use EmulatorTrait; use ValidateTrait; use RequestTrait; use RequestProcessorTrait; @@ -236,11 +238,20 @@ public function __construct(array $config = []) } else { $config['credentialsConfig']['scopes'] = $scopes; } - $config['credentials'] = $this->createCredentialsWrapper( - $config['credentials'], - $config['credentialsConfig'], - $config['universeDomain'] - ); + + if ($emulatorHost) { + $emulatorConfig = $this->emulatorGapicConfig($emulatorHost); + $config = array_merge( + $config, + $emulatorConfig + ); + } else { + $config['credentials'] = $this->createCredentialsWrapper( + $config['credentials'], + $config['credentialsConfig'], + $config['universeDomain'] + ); + } $this->projectId = $this->detectProjectId($config); $this->serializer = new Serializer([], [ 'google.protobuf.Value' => function ($v) { diff --git a/Spanner/tests/System/AdminTest.php b/Spanner/tests/System/AdminTest.php index b0808a53ca1f..98f64f6df584 100644 --- a/Spanner/tests/System/AdminTest.php +++ b/Spanner/tests/System/AdminTest.php @@ -95,7 +95,8 @@ public function testDatabase() $op = $instance->createDatabase($dbName); $this->assertInstanceOf(OperationResponse::class, $op); - $db = $op->pollUntilComplete(); + $op->pollUntilComplete(); + $db = $op->getResult(); $this->assertInstanceOf(Database::class, $db); self::$deletionQueue->add(function () use ($db) { diff --git a/Spanner/tests/System/PgWriteTest.php b/Spanner/tests/System/PgWriteTest.php index 395f154d55a5..27adf4902333 100644 --- a/Spanner/tests/System/PgWriteTest.php +++ b/Spanner/tests/System/PgWriteTest.php @@ -308,9 +308,10 @@ public function testWriteAndReadBackArrayValue($id, $field, $value) public function arrayFieldComplexValueProvider() { + $timestamp = new Timestamp(new \DateTime()); return [ [$this->randId(), 'arraybytesfield', [new Bytes('foo'), null, new Bytes('baz')]], - [$this->randId(), 'arraytimestampfield', [new Timestamp(new \DateTime()), null, new Timestamp(new \DateTime())]], + [$this->randId(), 'arraytimestampfield', [$timestamp, null, $timestamp]], [$this->randId(), 'arraydatefield', [new Date(new \DateTime()), null, new Date(new \DateTime())]], [$this->randId(), 'arraypgnumericfield', [new PgNumeric('0.12345'), null, new PgNumeric('12345')]], [$this->randId(), 'arraypgjsonbfield', [new PgJsonb('{"a":1.1,"b":"hello"}'), null, diff --git a/Spanner/tests/System/WriteTest.php b/Spanner/tests/System/WriteTest.php index 496177915e3c..193559ffd30f 100644 --- a/Spanner/tests/System/WriteTest.php +++ b/Spanner/tests/System/WriteTest.php @@ -342,9 +342,10 @@ public function testWriteAndReadBackFancyArrayValue($id, $field, $value) public function arrayFieldComplexValueProvider() { + $timestamp = new Timestamp(new \DateTime()); return [ [$this->randId(), 'arrayBytesField', [new Bytes('foo'), null, new Bytes('baz')]], - [$this->randId(), 'arrayTimestampField', [new Timestamp(new \DateTime()), null, new Timestamp(new \DateTime())]], + [$this->randId(), 'arrayTimestampField', [$timestamp, null, $timestamp]], [$this->randId(), 'arrayDateField', [new Date(new \DateTime()), null, new Date(new \DateTime())]], [$this->randId(), 'arrayNumericField', [new Numeric('0.12345'), null, new NUMERIC('12345')]], ]; diff --git a/Spanner/tests/Unit/BackupTest.php b/Spanner/tests/Unit/BackupTest.php index 6cf6f822febb..c5a8417586f4 100644 --- a/Spanner/tests/Unit/BackupTest.php +++ b/Spanner/tests/Unit/BackupTest.php @@ -86,7 +86,9 @@ public function setUp(): void ]); $this->database = $this->prophesize(Database::class); - $this->database->name()->willReturn(DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE)); + $this->database->name()->willReturn( + DatabaseAdminClient::databaseName(self::PROJECT_ID, self::INSTANCE, self::DATABASE) + ); $this->instance = $this->prophesize(Instance::class); $this->instance->name()->willReturn(DatabaseAdminClient::instanceName(self::PROJECT_ID, self::INSTANCE)); diff --git a/dev/composer.json b/dev/composer.json index 45d6640be076..db66b13b5f3d 100644 --- a/dev/composer.json +++ b/dev/composer.json @@ -24,6 +24,7 @@ "phpspec/prophecy-phpunit": "^2.0", "swaggest/json-schema": "^0.12.0" }, + "minimum-stability": "dev", "repositories": { "google-cloud": { "type": "path", From 2d711d3ba25277e2ec2a6abe35fbae36cb627386 Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Wed, 13 Nov 2024 19:47:20 +0000 Subject: [PATCH 4/8] add ExceptionMiddleware --- Core/src/Middleware/ExceptionMiddleware.php | 86 +++++++++++++ Spanner/src/Database.php | 113 +++++++++--------- Spanner/src/FormatKeySetTrait.php | 1 - Spanner/src/Result.php | 2 +- Spanner/src/SpannerClient.php | 8 ++ .../Unit/Session/CacheSessionPoolTest.php | 62 +++------- 6 files changed, 166 insertions(+), 106 deletions(-) create mode 100644 Core/src/Middleware/ExceptionMiddleware.php diff --git a/Core/src/Middleware/ExceptionMiddleware.php b/Core/src/Middleware/ExceptionMiddleware.php new file mode 100644 index 000000000000..d99a253db2fb --- /dev/null +++ b/Core/src/Middleware/ExceptionMiddleware.php @@ -0,0 +1,86 @@ +<?php +/* + * Copyright 2024 Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace Google\Cloud\Core\Middleware; + +use Google\ApiCore\ApiException; +use Google\ApiCore\BidiStream; +use Google\ApiCore\Call; +use Google\ApiCore\ClientStream; +use Google\ApiCore\Middleware\MiddlewareInterface; +use Google\ApiCore\ServerStream; +use Google\Cloud\Core\RequestProcessorTrait; +use GuzzleHttp\Promise\PromiseInterface; +use Throwable; + +/** + * Middleware that adds autopopulation functionality. This middlware is + * added iff auto population settings are present in the resource + * descriptor config for the rpc method in context. + * + * @internal + */ +class ExceptionMiddleware implements MiddlewareInterface +{ + use RequestProcessorTrait; + + /** @var callable */ + private $nextHandler; + + public function __construct(callable $nextHandler) { + $this->nextHandler = $nextHandler; + } + + /** + * @param Call $call + * @param array $options + * + * @return PromiseInterface|ClientStream|ServerStream|BidiStream + */ + public function __invoke(Call $call, array $options) + { + $response = ($this->nextHandler)($call, $options); + if ($response instanceof PromiseInterface) { + return $response->then(null, function ($value) { + if ($value instanceof \Google\ApiCore\ApiException) { + throw $this->convertToGoogleException($value); + } + if ($value instanceof Throwable) { + throw $value; + } + }); + } + // this can also be a Stream + return $response; + } +} + diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index a19fe541ed99..3a90431f3e26 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -237,7 +237,7 @@ public function __construct( * @param array $options [optional] Configuration options. * @return int|null */ - public function state(array $options = []) + public function state(array $options = []): ?int { $info = $this->info($options); @@ -269,7 +269,7 @@ public function state(array $options = []) * * @return ItemIterator<Backup> */ - public function backups(array $options = []) + public function backups(array $options = []): ItemIterator { $filter = 'database:' . $this->name(); @@ -297,9 +297,9 @@ public function backups(array $options = []) * eligible to be automatically deleted by Cloud Spanner. * @param array $options [optional] Configuration options. * - * @return OperationResponse + * @return OperationResponse<Backup> */ - public function createBackup($name, \DateTimeInterface $expireTime, array $options = []) + public function createBackup($name, \DateTimeInterface $expireTime, array $options = []): OperationResponse { $backup = $this->instance->backup($name); return $backup->create($this->name(), $expireTime, $options); @@ -315,7 +315,7 @@ public function createBackup($name, \DateTimeInterface $expireTime, array $optio * * @return string */ - public function name() + public function name(): string { return $this->name; } @@ -335,7 +335,7 @@ public function name() * @param array $options [optional] Configuration options. * @return array */ - public function info(array $options = []) + public function info(array $options = []): array { return $this->info ?: $this->reload($options); } @@ -355,7 +355,7 @@ public function info(array $options = []) * @param array $options [optional] Configuration options. * @return array */ - public function reload(array $options = []) + public function reload(array $options = []): array { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['name'] = $this->name; @@ -384,7 +384,7 @@ public function reload(array $options = []) * @param array $options [optional] Configuration options. * @return bool */ - public function exists(array $options = []) + public function exists(array $options = []): bool { try { $this->reload($options); @@ -414,10 +414,10 @@ public function exists(array $options = []) * } * @return OperationResponse<Database> */ - public function create(array $options = []) + public function create(array $options = []): OperationResponse { list($data, $callOptions) = $this->splitOptionalArgs($options); - $dialect = $data['databaseDialect'] ?? null; + $dialect = $data['databaseDialect'] ?? DatabaseDialect::DATABASE_DIALECT_UNSPECIFIED; $data += [ 'parent' => $this->instance->name(), @@ -448,7 +448,7 @@ public function create(array $options = []) * * @return OperationResponse<Database> */ - public function restore($backup, array $options = []) + public function restore($backup, array $options = []): OperationResponse { return $this->instance->createDatabaseFromBackup($this->name, $backup, $options); } @@ -471,9 +471,9 @@ public function restore($backup, array $options = []) * @type bool $enableDropProtection If `true`, delete operations for Database * and Instance will be blocked. **Defaults to** `false`. * } - * @return OperationResponse + * @return OperationResponse<Database> */ - public function updateDatabase(array $options = []) + public function updateDatabase(array $options = []): OperationResponse { list($data, $callOptions) = $this->splitOptionalArgs($options); $fieldMask = []; @@ -520,9 +520,9 @@ public function updateDatabase(array $options = []) * * @param string $statement A DDL statements to run against a database. * @param array $options [optional] Configuration options. - * @return OperationResponse + * @return OperationResponse<Database> */ - public function updateDdl($statement, array $options = []) + public function updateDdl($statement, array $options = []): OperationResponse { return $this->updateDdlBatch([$statement], $options); } @@ -555,9 +555,9 @@ public function updateDdl($statement, array $options = []) * * @param string[] $statements A list of DDL statements to run against a database. * @param array $options [optional] Configuration options. - * @return OperationResponse + * @return OperationResponse<void> */ - public function updateDdlBatch(array $statements, array $options = []) + public function updateDdlBatch(array $statements, array $options = []): OperationResponse { list($data, $callOptions) = $this->splitOptionalArgs($options); $data += [ @@ -593,7 +593,7 @@ public function updateDdlBatch(array $statements, array $options = []) * @param array $options [optional] Configuration options. * @return void */ - public function drop(array $options = []) + public function drop(array $options = []): void { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['database'] = $this->name; @@ -630,7 +630,7 @@ public function drop(array $options = []) * @param array $options [optional] Configuration options. * @return array */ - public function ddl(array $options = []) + public function ddl(array $options = []): array { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['database'] = $this->name; @@ -735,7 +735,7 @@ public function iam() * an existing transaction. * @codingStandardsIgnoreEnd */ - public function snapshot(array $options = []) + public function snapshot(array $options = []): Snapshot { if ($this->isRunningTransaction) { throw new \BadMethodCallException('Nested transactions are not supported by this client.'); @@ -800,7 +800,7 @@ public function snapshot(array $options = []) * @throws \BadMethodCallException If attempting to call this method within * an existing transaction. */ - public function transaction(array $options = []) + public function transaction(array $options = []): Transaction { if ($this->isRunningTransaction) { throw new \BadMethodCallException('Nested transactions are not supported by this client.'); @@ -1033,7 +1033,7 @@ public function runTransaction(callable $operation, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function insert($table, array $data, array $options = []) + public function insert(string $table, array $data, array $options = []): Timestamp { return $this->insertBatch($table, [$data], $options); } @@ -1082,7 +1082,7 @@ public function insert($table, array $data, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function insertBatch($table, array $dataSet, array $options = []) + public function insertBatch(string $table, array $dataSet, array $options = []): Timestamp { $mutations = []; foreach ($dataSet as $data) { @@ -1128,7 +1128,7 @@ public function insertBatch($table, array $dataSet, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function update($table, array $data, array $options = []) + public function update(string $table, array $data, array $options = []): Timestamp { return $this->updateBatch($table, [$data], $options); } @@ -1174,7 +1174,7 @@ public function update($table, array $data, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function updateBatch($table, array $dataSet, array $options = []) + public function updateBatch(string $table, array $dataSet, array $options = []): Timestamp { $mutations = []; foreach ($dataSet as $data) { @@ -1221,7 +1221,7 @@ public function updateBatch($table, array $dataSet, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function insertOrUpdate($table, array $data, array $options = []) + public function insertOrUpdate(string $table, array $data, array $options = []): Timestamp { return $this->insertOrUpdateBatch($table, [$data], $options); } @@ -1269,7 +1269,7 @@ public function insertOrUpdate($table, array $data, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function insertOrUpdateBatch($table, array $dataSet, array $options = []) + public function insertOrUpdateBatch(string $table, array $dataSet, array $options = []): Timestamp { $mutations = []; foreach ($dataSet as $data) { @@ -1316,7 +1316,7 @@ public function insertOrUpdateBatch($table, array $dataSet, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function replace($table, array $data, array $options = []) + public function replace(string $table, array $data, array $options = []): Timestamp { return $this->replaceBatch($table, [$data], $options); } @@ -1364,7 +1364,7 @@ public function replace($table, array $data, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function replaceBatch($table, array $dataSet, array $options = []) + public function replaceBatch($table, array $dataSet, array $options = []): Timestamp { $mutations = []; foreach ($dataSet as $data) { @@ -1414,7 +1414,7 @@ public function replaceBatch($table, array $dataSet, array $options = []) * } * @return Timestamp The commit Timestamp. */ - public function delete($table, KeySet $keySet, array $options = []) + public function delete($table, KeySet $keySet, array $options = []): Timestamp { $mutations = [$this->operation->deleteMutation($table, $keySet)]; @@ -1673,7 +1673,7 @@ public function delete($table, KeySet $keySet, array $options = []) * @codingStandardsIgnoreEnd * @return Result */ - public function execute($sql, array $options = []) + public function execute($sql, array $options = []): Result { unset($options['requestOptions']['transactionTag']); $session = $this->pluck('session', $options, false) @@ -1707,7 +1707,7 @@ public function execute($sql, array $options = []) * * @return MutationGroup */ - public function mutationGroup() + public function mutationGroup(): MutationGroup { return new MutationGroup($this->returnInt64AsObject); } @@ -1754,11 +1754,11 @@ public function mutationGroup() * transactions. * } * - * @retur \Generator {@see \Google\Cloud\Spanner\V1\BatchWriteResponse} + * @return \Generator {@see \Google\Cloud\Spanner\V1\BatchWriteResponse} * * @throws ApiException if the remote call fails */ - public function batchWrite(array $mutationGroups, array $options = []) + public function batchWrite(array $mutationGroups, array $options = []): \Generator { if ($this->isRunningTransaction) { throw new \BadMethodCallException('Nested transactions are not supported by this client.'); @@ -1913,7 +1913,7 @@ public function batchWrite(array $mutationGroups, array $options = []) * } * @return int The number of rows modified. */ - public function executePartitionedUpdate($statement, array $options = []) + public function executePartitionedUpdate($statement, array $options = []): int { unset($options['requestOptions']['transactionTag']); $session = $this->selectSession(SessionPoolInterface::CONTEXT_READWRITE); @@ -2058,7 +2058,7 @@ public function executePartitionedUpdate($statement, array $options = []) * @codingStandardsIgnoreEnd * @return Result */ - public function read($table, KeySet $keySet, array $columns, array $options = []) + public function read($table, KeySet $keySet, array $columns, array $options = []): Result { unset($options['requestOptions']['transactionTag']); $session = $this->selectSession( @@ -2096,7 +2096,7 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] * * @return SessionPoolInterface|null */ - public function sessionPool() + public function sessionPool(): ?SessionPoolInterface { return $this->sessionPool; } @@ -2114,7 +2114,7 @@ public function sessionPool() * $database->close(); * ``` */ - public function close() + public function close(): void { if ($this->session) { if ($this->sessionPool) { @@ -2152,7 +2152,7 @@ public function __destruct() * @param array $options [optional] Configuration options. * @return Session */ - public function createSession(array $options = []) + public function createSession(array $options = []): Session { return $this->operation->createSession($this->name, $options); } @@ -2169,7 +2169,7 @@ public function createSession(array $options = []) * @param string $sessionName The session's name. * @return Session */ - public function session($sessionName) + public function session(string $sessionName): Session { return $this->operation->session($sessionName); } @@ -2180,7 +2180,7 @@ public function session($sessionName) * @access private * @return array */ - public function identity() + public function identity(): array { $databaseParts = explode('/', $this->name); $instanceParts = explode('/', $this->instance->name()); @@ -2200,7 +2200,7 @@ public function identity() * @type int $sessionCount * } */ - public function batchCreateSessions(array $options) + public function batchCreateSessions(array $options): array { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['database'] = $this->name; @@ -2210,6 +2210,7 @@ public function batchCreateSessions(array $options) $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); $response = $this->spannerClient->batchCreateSessions($request, $callOptions); + return $this->handleResponse($response); } /** @@ -2219,10 +2220,10 @@ public function batchCreateSessions(array $options) * @param array $options { * @type name The session name to be deleted * } - * @return PromiseInterface + * @return PromiseInterface<void> * @experimental */ - public function deleteSessionAsync(array $options) + public function deleteSessionAsync(array $options): PromiseInterface { list($data, $callOptions) = $this->splitOptionalArgs($options); @@ -2253,7 +2254,7 @@ public function deleteSessionAsync(array $options) * * @return ItemIterator<OperationResponse> */ - public function backupOperations(array $options = []) + public function backupOperations(array $options = []): ItemIterator { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['parent'] = $this->instance->name(); @@ -2298,7 +2299,7 @@ function ($callOptions) use ($data) { * * @return OperationResponse */ - public function createDatabaseFromBackup($name, $backup, array $options = []) + public function createDatabaseFromBackup($name, $backup, array $options = []): OperationResponse { list($data, $callOptions) = $this->splitOptionalArgs($options); $data += [ @@ -2335,7 +2336,7 @@ public function createDatabaseFromBackup($name, $backup, array $options = []) * * @return ItemIterator<OperationResponse> */ - public function databaseOperations(array $options = []) + public function databaseOperations(array $options = []): ItemIterator { list($data, $callOptions) = $this->splitOptionalArgs($options); $data['parent'] = $this->instance->name(); @@ -2380,7 +2381,7 @@ function ($callOptions) use ($data) { * @param string $operationName The Long Running Operation name. * @return OperationResponse */ - public function resumeOperation($operationName) + public function resumeOperation($operationName): OperationResponse { return new OperationResponse( $operationName, @@ -2398,7 +2399,7 @@ public function resumeOperation($operationName) * @param array $options [optional] Configuration options. * @return Session */ - private function selectSession($context = SessionPoolInterface::CONTEXT_READ, array $options = []) + private function selectSession($context = SessionPoolInterface::CONTEXT_READ, array $options = []): Session { if ($this->session) { return $this->session; @@ -2431,7 +2432,7 @@ private function selectSession($context = SessionPoolInterface::CONTEXT_READ, ar * } * @return Timestamp The commit timestamp. */ - private function commitInSingleUseTransaction(array $mutations, array $options = []) + private function commitInSingleUseTransaction(array $mutations, array $options = []): Timestamp { unset($options['requestOptions']['transactionTag']); $options['mutations'] = $mutations; @@ -2448,7 +2449,7 @@ private function commitInSingleUseTransaction(array $mutations, array $options = * * @return string */ - private function fullyQualifiedDatabaseName($name) + private function fullyQualifiedDatabaseName($name): string { $instance = DatabaseAdminClient::parseName($this->instance->name())['instance']; @@ -2468,10 +2469,10 @@ private function fullyQualifiedDatabaseName($name) /** * Returns the 'CREATE DATABASE' statement as per the given database dialect * - * @param string $dialect The dialect of the database to be created + * @param int $dialect The dialect of the database to be created * @return string The specific 'CREATE DATABASE' statement */ - private function getCreateDbStatement($dialect) + private function getCreateDbStatement(int $dialect): string { $databaseId = DatabaseAdminClient::parseName($this->name())['database']; @@ -2488,7 +2489,7 @@ private function getCreateDbStatement($dialect) * @param string $name The database name or id. * @return string */ - private function databaseIdOnly($name) + private function databaseIdOnly(string $name): string { try { return DatabaseAdminClient::parseName($name)['database']; @@ -2497,7 +2498,7 @@ private function databaseIdOnly($name) } } - private function parseMutations($rawMutations) + private function parseMutations(array $rawMutations): array { if (!is_array($rawMutations)) { return []; @@ -2549,7 +2550,7 @@ private function parseMutations($rawMutations) * @param mixed $param * @return Value */ - private function fieldValue($param) + private function fieldValue($param): Value { $field = new Value(); $value = $this->formatValueForApi($param); diff --git a/Spanner/src/FormatKeySetTrait.php b/Spanner/src/FormatKeySetTrait.php index 4607bd5d12d8..de79d008f21f 100644 --- a/Spanner/src/FormatKeySetTrait.php +++ b/Spanner/src/FormatKeySetTrait.php @@ -35,4 +35,3 @@ private function formatKeySet(array $keySet) return $keySet; } } - diff --git a/Spanner/src/Result.php b/Spanner/src/Result.php index f5b9c6d5ae53..c15b6fff0901 100644 --- a/Spanner/src/Result.php +++ b/Spanner/src/Result.php @@ -183,7 +183,7 @@ public function __construct( * @throws \RuntimeException When duplicate column names exist with a * selected format of `Result::RETURN_ASSOCIATIVE`. */ - public function rows($format = self::RETURN_ASSOCIATIVE) + public function rows($format = self::RETURN_ASSOCIATIVE): \Generator { $bufferedResults = []; $call = $this->call; diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 82c4072ef44b..a5147244718f 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -19,6 +19,7 @@ use Google\ApiCore\ClientOptionsTrait; use Google\ApiCore\CredentialsWrapper; +use Google\ApiCore\Middleware\MiddlewareInterface; use Google\ApiCore\OperationResponse; use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; @@ -30,6 +31,7 @@ use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; +use Google\Cloud\Core\Middleware\ExceptionMiddleware; use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; @@ -274,11 +276,17 @@ public function __construct(array $config = []) 'libName' => 'gccl', 'serializer' => $this->serializer, ]; + $middleware = function (MiddlewareInterface $handler) { + return new ExceptionMiddleware($handler); + }; $this->spannerClient = $config['gapicSpannerClient'] ?? new GapicSpannerClient($clientConfig); + $this->spannerClient->addMiddleware($middleware); $this->instanceAdminClient = $config['gapicSpannerInstanceAdminClient'] ?? new InstanceAdminClient($clientConfig); + $this->instanceAdminClient->addMiddleware($middleware); $this->databaseAdminClient = $config['gapicSpannerDatabaseAdminClient'] ?? new DatabaseAdminClient($clientConfig); + $this->databaseAdminClient->addMiddleware($middleware); $this->projectName = InstanceAdminClient::projectName($this->projectId); } diff --git a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php index 3ca6a4c88564..728036b3e488 100644 --- a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php +++ b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php @@ -22,8 +22,11 @@ use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Lock\MockValues; use Google\Cloud\Spanner\Database; +use Google\Cloud\Spanner\Result; use Google\Cloud\Spanner\Session\CacheSessionPool; use Google\Cloud\Spanner\Session\Session; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; +use Google\Protobuf\GPBEmpty; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\RejectedPromise; use PHPUnit\Framework\TestCase; @@ -42,6 +45,7 @@ class CacheSessionPoolTest extends TestCase { use GrpcTestTrait; use ProphecyTrait; + use ResultGeneratorTrait; const CACHE_KEY_TEMPLATE = CacheSessionPool::CACHE_KEY_TEMPLATE; const PROJECT_ID = 'project'; @@ -856,9 +860,11 @@ public function acquireDataProvider() private function getDatabase($shouldCreateFails = false, $willDeleteSessions = false, $expectedCreateCalls = null) { - $database = $this->prophesize(DatabaseStub::class); - $session = $this->prophesize(SessionStub::class); + $database = $this->prophesize(Database::class); + $session = $this->prophesize(Session::class); $requestHandler = $this->prophesize(RequestHandler::class); + $result = $this->prophesize(Result::class); + $result->rows()->willReturn($this->resultGeneratorChunks([])); $session->expiration() ->willReturn($this->time + 3600); @@ -866,16 +872,12 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f ->willReturn(false); if ($willDeleteSessions) { $session->delete() - ->willReturn(null); + ->willReturn(null); $database->deleteSessionAsync(Argument::any()) - ->willReturn(new FulfilledPromise( - new DumbObject() - )); + ->willReturn(new FulfilledPromise(new GPBEmpty())); } else { $database->deleteSessionAsync(Argument::any()) - ->willReturn(new RejectedPromise( - new DumbObject() - )); + ->willReturn(new RejectedPromise(new GPBEmpty())); } $database->session(Argument::any()) @@ -894,7 +896,7 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f $database->name() ->willReturn(self::DATABASE_NAME); $database->execute(Argument::exact('SELECT 1'), Argument::withKey('session')) - ->willReturn(new DumbObject()); + ->willReturn($result->reveal()); $createRes = function ($args, $mock, $method) use ($shouldCreateFails) { if ($shouldCreateFails) { @@ -1001,7 +1003,7 @@ public function testMaintainEmptyData() public function testMaintainException() { $data = $this->cacheData(['dead' => 3700, 'old' => 3200, 'fresh' => 100, 'other' => 1500], 300); - $database = $this->prophesize(DatabaseStub::class); + $database = $this->prophesize(Database::class); $database->identity()->willReturn([ 'projectId' => self::PROJECT_ID, 'database' => self::DATABASE_NAME, @@ -1179,7 +1181,7 @@ public function testSessionPoolDatabaseRole() $config = ['minSessions' => 1, 'databaseRole' => 'Reader']; $cache = $this->getCacheItemPool($initialData); $pool = new CacheSessionPoolStub($cache, $config, $this->time); - $database = $this->prophesize(DatabaseStub::class); + $database = $this->prophesize(Database::class); $database->identity() ->willReturn([ 'projectId' => self::PROJECT_ID, @@ -1214,40 +1216,4 @@ protected function time() return $this->time ?: parent::time(); } } - -class DatabaseStub extends Database -{ - // prevent "get_class() expects parameter 1 to be object" warning when debugging - public function __debugInfo() - { - return []; - } -} - -class SessionStub extends Session -{ - // prevent "get_class() expects parameter 1 to be object" warning when debugging - public function __debugInfo() - { - return []; - } -} - -class DumbObject -{ - public function __get($name) - { - return $this; - } - - public function __call($name, $args) - { - return $this; - } - - public function serializeToString() - { - return ''; - } -} //@codingStandardsIgnoreEnd From 278b347cee060a27223ed9d9429ffcd80251f69c Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Wed, 13 Nov 2024 13:31:52 -0800 Subject: [PATCH 5/8] refactor Serializer --- Core/src/Serializer.php | 46 +++++++++++++ Spanner/src/Backup.php | 2 +- Spanner/src/Database.php | 2 +- Spanner/src/Instance.php | 2 +- Spanner/src/InstanceConfiguration.php | 2 +- Spanner/src/Operation.php | 2 +- Spanner/src/Session/Session.php | 2 +- Spanner/src/SpannerClient.php | 45 +++++++++---- Spanner/tests/Snippet/ArrayTypeTest.php | 66 +++++++++++-------- .../tests/Snippet/Batch/BatchClientTest.php | 2 +- Spanner/tests/Unit/BackupTest.php | 17 +---- Spanner/tests/Unit/Batch/BatchClientTest.php | 17 +---- .../tests/Unit/Batch/BatchSnapshotTest.php | 17 +---- Spanner/tests/Unit/DatabaseTest.php | 17 +---- .../tests/Unit/InstanceConfigurationTest.php | 2 +- Spanner/tests/Unit/InstanceTest.php | 2 +- Spanner/tests/Unit/OperationTest.php | 17 +---- .../Unit/Session/CacheSessionPoolTest.php | 2 +- Spanner/tests/Unit/SpannerClientTest.php | 2 +- Spanner/tests/Unit/TransactionTest.php | 17 +---- Spanner/tests/Unit/TransactionTypeTest.php | 2 +- 21 files changed, 142 insertions(+), 141 deletions(-) create mode 100644 Core/src/Serializer.php diff --git a/Core/src/Serializer.php b/Core/src/Serializer.php new file mode 100644 index 000000000000..e8e194b93c3f --- /dev/null +++ b/Core/src/Serializer.php @@ -0,0 +1,46 @@ +<?php + +namespace Google\Cloud\Core; + +use Google\ApiCore\Serializer as ApiCoreSerializer; +use Google\Cloud\Core\ApiHelperTrait; + +/** + * @internal + * Supplies helper methods to interact with the APIs. + */ +class Serializer extends ApiCoreSerializer +{ + use ApiHelperTrait; + + public function __construct( + $fieldTransformers = [], + $messageTypeTransformers = [], + $decodeFieldTransformers = [], + $decodeMessageTypeTransformers = [], + $customEncoders = [], + ) { + $messageTypeTransformers += [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]; + + parent::__construct( + $fieldTransformers, + $messageTypeTransformers, + $decodeFieldTransformers, + $decodeMessageTypeTransformers, + $customEncoders, + ); + } +} \ No newline at end of file diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index 14f404aa2d04..fd210a3ca877 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -20,7 +20,7 @@ use Closure; use DateTimeInterface; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index 3a90431f3e26..c366c8e55878 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -21,7 +21,7 @@ use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; use Google\ApiCore\RetrySettings; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 80698e573a47..d0c50af270b7 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -20,7 +20,7 @@ use Closure; use Google\ApiCore\ArrayTrait; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index 7b0324ccadcf..002bbe8a39fe 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -20,7 +20,7 @@ use Closure; use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\RequestProcessorTrait; diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index a3dce8988a7b..f83c6321c478 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner; use Google\ApiCore\ArrayTrait; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Core\TimeTrait; diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index a59120dc75cc..ead94f85870f 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner\Session; use Google\ApiCore\ArrayTrait; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Spanner\Database; diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index a5147244718f..db83ef5efad0 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -21,7 +21,6 @@ use Google\ApiCore\CredentialsWrapper; use Google\ApiCore\Middleware\MiddlewareInterface; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Auth\FetchAuthTokenInterface; use Google\Cloud\Core\ApiHelperTrait; @@ -33,6 +32,7 @@ use Google\Cloud\Core\Iterator\PageIterator; use Google\Cloud\Core\Middleware\ExceptionMiddleware; use Google\Cloud\Core\RequestProcessorTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; @@ -255,18 +255,37 @@ public function __construct(array $config = []) ); } $this->projectId = $this->detectProjectId($config); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); + $this->serializer = new Serializer([], [], [], [], [ + // A custom encoder that short-circuits the encodeMessage in Serializer class, + // but only if the argument is of the type PartialResultSet. + PartialResultSet::class => function ($msg) { + $data = json_decode($msg->serializeToJsonString(), true); + + // We only override metadata fields, if it actually exists in the response. + // This is specially important for large data sets which is received in chunks. + // Metadata is only received in the first 'chunk' and we don't want to set empty metadata fields + // when metadata was not returned from the server. + if (isset($data['metadata'])) { + // The transaction id is serialized as a base64 encoded string in $data. So, we + // add a step to get the transaction id using a getter instead of the serialized value. + // The null-safe operator is used to handle edge cases where the relevant fields are not present. + $data['metadata']['transaction']['id'] = (string) $msg?->getMetadata()?->getTransaction()?->getId(); + + // Helps convert metadata enum values from string types to their respective code/annotation + // pairs. Ex: INT64 is converted to {code: 2, typeAnnotation: 0}. + $fields = $msg->getMetadata()?->getRowType()?->getFields(); + $data['metadata']['rowType']['fields'] = $this->getFieldDataFromRepeatedFields($fields); + } + + // These fields in stats should be an int + if (isset($data['stats']['rowCountLowerBound'])) { + $data['stats']['rowCountLowerBound'] = (int) $data['stats']['rowCountLowerBound']; + } + if (isset($data['stats']['rowCountExact'])) { + $data['stats']['rowCountExact'] = (int) $data['stats']['rowCountExact']; + } + + return $data; } ]); diff --git a/Spanner/tests/Snippet/ArrayTypeTest.php b/Spanner/tests/Snippet/ArrayTypeTest.php index a5daf6276f32..205e35f1f208 100644 --- a/Spanner/tests/Snippet/ArrayTypeTest.php +++ b/Spanner/tests/Snippet/ArrayTypeTest.php @@ -17,18 +17,21 @@ namespace Google\Cloud\Spanner\Tests\Snippet; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Spanner\Admin\Database\V1\DatabaseAdminClient; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\ArrayType; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\StructType; +use Google\Cloud\Spanner\Tests\ResultGeneratorTrait; use Google\Cloud\Spanner\V1\Client\SpannerClient; +use Google\Cloud\Spanner\V1\PartialResultSet; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -40,6 +43,8 @@ class ArrayTypeTest extends SnippetTestCase { use GrpcTestTrait; use ProphecyTrait; + use ApiHelperTrait; + use ResultGeneratorTrait; const PROJECT = 'my-awesome-project'; const DATABASE = 'my-database'; @@ -47,6 +52,7 @@ class ArrayTypeTest extends SnippetTestCase private $database; private $type; + private $spannerClient; public function setUp(): void { @@ -72,9 +78,11 @@ public function setUp(): void $sessionPool->setDatabase(Argument::any()) ->willReturn(null); + $this->spannerClient = $this->prophesize(SpannerClient::class); $this->serializer = new Serializer(); + $this->database = new Database( - $this->prophesize(SpannerClient::class)->reveal(), + $this->spannerClient->reveal(), $this->prophesize(DatabaseAdminClient::class)->reveal(), $this->serializer, $instance->reveal(), @@ -93,15 +101,13 @@ public function testConstructor() ] ]; - $values = [ - 'foo', 'bar', null - ]; - $this->spannerClient->executeStreamingSql( Argument::that(function ($args) use ($values, $field) { $message = $this->serializer->encodeMessage($args); $this->assertEquals($message['sql'], 'SELECT @arrayParam as arrayValue'); - $this->assertEquals($message['params'], ['arrayParam' => $values]); + $this->assertEquals($message['params'], ['arrayParam' => [ + 'foo', 'bar', null + ]]); $this->assertEquals( $message['paramTypes']['arrayParam']['arrayElementType']['code'], $field['arrayElementType']['code'] @@ -112,23 +118,36 @@ public function testConstructor() ); return true; }), - $this->resultGenerator([ - 'metadata' => [ - 'rowType' => [ - 'fields' => [ - [ - 'name' => 'arrayValue', - 'type' => $field + Argument::type('array') + )->shouldBeCalled()->willReturn( + $this->resultGeneratorStream([$this->serializer->decodeMessage( + new PartialResultSet(), + [ + 'metadata' => [ + 'rowType' => [ + 'fields' => [ + [ + 'name' => 'arrayValue', + 'type' => $field + ] + ] + ] + ], + 'values' => [ + [ + 'listValue' => [ + 'values' => [ + ['stringValue' => 'foo'], + ['stringValue' => 'bar'], + ['nullValue' => 0] + ] ] ] ] - ], - 'values' => [$values] - ]) + ] + )->serializeToJsonString()]) ); - $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); - $snippet = $this->snippetFromClass(ArrayType::class); $snippet->replace('$database = $spanner->connect(\'my-instance\', \'my-database\');', ''); $snippet->addLocal('database', $this->database); @@ -144,9 +163,4 @@ public function testArrayTypeStruct() $this->assertEquals(Database::TYPE_STRUCT, $res->type()); $this->assertInstanceOf(StructType::class, $res->structType()); } - - private function resultGenerator(array $data) - { - yield $data; - } } diff --git a/Spanner/tests/Snippet/Batch/BatchClientTest.php b/Spanner/tests/Snippet/Batch/BatchClientTest.php index ab84a0c3e08a..9d034ee56b71 100644 --- a/Spanner/tests/Snippet/Batch/BatchClientTest.php +++ b/Spanner/tests/Snippet/Batch/BatchClientTest.php @@ -17,7 +17,7 @@ namespace Google\Cloud\Spanner\Tests\Snippet\Batch; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\RequestHandler; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; diff --git a/Spanner/tests/Unit/BackupTest.php b/Spanner/tests/Unit/BackupTest.php index c5a8417586f4..c36f7f3de4b8 100644 --- a/Spanner/tests/Unit/BackupTest.php +++ b/Spanner/tests/Unit/BackupTest.php @@ -20,8 +20,8 @@ use DateTime; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; @@ -70,20 +70,7 @@ public function setUp(): void $this->checkAndSkipGrpcTests(); $this->databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->database = $this->prophesize(Database::class); $this->database->name()->willReturn( diff --git a/Spanner/tests/Unit/Batch/BatchClientTest.php b/Spanner/tests/Unit/Batch/BatchClientTest.php index 1f37dfbdfa16..ef1ab09189aa 100644 --- a/Spanner/tests/Unit/Batch/BatchClientTest.php +++ b/Spanner/tests/Unit/Batch/BatchClientTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; -use Google\ApiCore\Serializer; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Timestamp; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\Batch\BatchClient; @@ -59,20 +59,7 @@ class BatchClientTest extends TestCase public function setUp(): void { - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->spannerClient = $this->prophesize(GapicSpannerClient::class); $this->batchClient = new BatchClient( new Operation($this->spannerClient->reveal(), $this->serializer, false), diff --git a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php index 54f7018526b0..f610391b8384 100644 --- a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php @@ -17,8 +17,8 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; -use Google\ApiCore\Serializer; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\PartitionInterface; use Google\Cloud\Spanner\Batch\QueryPartition; @@ -73,20 +73,7 @@ public function setUp(): void $this->timestamp = new Timestamp(new \DateTime()); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->spannerClient = $this->prophesize(SpannerClient::class); $this->snapshot = new BatchSnapshot( diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index 611e215f7d90..be968c911dc1 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -20,7 +20,6 @@ use Google\ApiCore\OperationResponse; use Google\ApiCore\Page; use Google\ApiCore\PagedListResponse; -use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; @@ -29,6 +28,7 @@ use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\Fixtures; use Google\Cloud\Spanner\Admin\Database\V1\Backup; @@ -142,20 +142,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->sessionPool = $this->prophesize(SessionPoolInterface::class); $this->spannerClient = $this->prophesize(SpannerClient::class); diff --git a/Spanner/tests/Unit/InstanceConfigurationTest.php b/Spanner/tests/Unit/InstanceConfigurationTest.php index 7ae740db6fed..ce9bb8127289 100644 --- a/Spanner/tests/Unit/InstanceConfigurationTest.php +++ b/Spanner/tests/Unit/InstanceConfigurationTest.php @@ -19,7 +19,7 @@ use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; diff --git a/Spanner/tests/Unit/InstanceTest.php b/Spanner/tests/Unit/InstanceTest.php index bd48de91ab62..1f8378fc06d9 100644 --- a/Spanner/tests/Unit/InstanceTest.php +++ b/Spanner/tests/Unit/InstanceTest.php @@ -20,7 +20,7 @@ use Google\ApiCore\OperationResponse; use Google\ApiCore\Page; use Google\ApiCore\PagedListResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; diff --git a/Spanner/tests/Unit/OperationTest.php b/Spanner/tests/Unit/OperationTest.php index 2a4a85acaadd..12bcc4dc084c 100644 --- a/Spanner/tests/Unit/OperationTest.php +++ b/Spanner/tests/Unit/OperationTest.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\ApiCore\Serializer; use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; @@ -79,20 +79,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->spannerClient = $this->prophesize(SpannerClient::class); $this->operation = new Operation( diff --git a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php index 728036b3e488..c80122b7ae5f 100644 --- a/Spanner/tests/Unit/Session/CacheSessionPoolTest.php +++ b/Spanner/tests/Unit/Session/CacheSessionPoolTest.php @@ -864,7 +864,7 @@ private function getDatabase($shouldCreateFails = false, $willDeleteSessions = f $session = $this->prophesize(Session::class); $requestHandler = $this->prophesize(RequestHandler::class); $result = $this->prophesize(Result::class); - $result->rows()->willReturn($this->resultGeneratorChunks([])); + $result->rows()->willReturn($this->resultGeneratorJson([])); $session->expiration() ->willReturn($this->time + 3600); diff --git a/Spanner/tests/Unit/SpannerClientTest.php b/Spanner/tests/Unit/SpannerClientTest.php index 526d3cc6da18..03eed6411aac 100644 --- a/Spanner/tests/Unit/SpannerClientTest.php +++ b/Spanner/tests/Unit/SpannerClientTest.php @@ -20,7 +20,7 @@ use Google\ApiCore\OperationResponse; use Google\ApiCore\Page; use Google\ApiCore\PagedListResponse; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; diff --git a/Spanner/tests/Unit/TransactionTest.php b/Spanner/tests/Unit/TransactionTest.php index 145639340447..2eeaf0e6d45e 100644 --- a/Spanner/tests/Unit/TransactionTest.php +++ b/Spanner/tests/Unit/TransactionTest.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\ApiCore\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\BatchDmlResult; @@ -81,20 +81,7 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); - $this->serializer = new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); + $this->serializer = new Serializer(); $this->spannerClient = $this->prophesize(SpannerClient::class); $this->operation = new Operation( $this->spannerClient->reveal(), diff --git a/Spanner/tests/Unit/TransactionTypeTest.php b/Spanner/tests/Unit/TransactionTypeTest.php index 725a0b260986..65ee16096306 100644 --- a/Spanner/tests/Unit/TransactionTypeTest.php +++ b/Spanner/tests/Unit/TransactionTypeTest.php @@ -17,7 +17,7 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\ApiCore\Serializer; +use Google\Cloud\Core\Serializer; use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; From 0e1646713f6e723e022dc92121e6141103e1d13f Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Wed, 13 Nov 2024 16:33:04 -0800 Subject: [PATCH 6/8] WIP --- Core/src/Iterator/PageIteratorTrait.php | 1 + Core/src/Testing/Snippet/Parser/Snippet.php | 1 + Spanner/src/Backup.php | 64 ++++- Spanner/tests/Snippet/ArrayTypeTest.php | 31 ++- Spanner/tests/Snippet/BackupTest.php | 227 +++++++++--------- .../tests/Snippet/Batch/BatchClientTest.php | 1 - .../tests/Snippet/Batch/BatchSnapshotTest.php | 1 - Spanner/tests/Snippet/CommitTimestampTest.php | 38 +-- Spanner/tests/Snippet/DatabaseTest.php | 178 ++++++-------- .../Snippet/InstanceConfigurationTest.php | 38 +-- Spanner/tests/Snippet/InstanceTest.php | 151 ++++++------ Spanner/tests/Snippet/SpannerClientTest.php | 36 +-- Spanner/tests/Snippet/TransactionTest.php | 1 - 13 files changed, 418 insertions(+), 350 deletions(-) diff --git a/Core/src/Iterator/PageIteratorTrait.php b/Core/src/Iterator/PageIteratorTrait.php index bbf0ff8e1bd6..9c608460bf86 100644 --- a/Core/src/Iterator/PageIteratorTrait.php +++ b/Core/src/Iterator/PageIteratorTrait.php @@ -260,6 +260,7 @@ private function mapResults(array $results) if ($items) { foreach ($items as $key => $item) { $items[$key] = $resultMapper($item); + $this->itemCount++; if ($this->config['resultLimit'] && $this->config['resultLimit'] <= $this->itemCount) { diff --git a/Core/src/Testing/Snippet/Parser/Snippet.php b/Core/src/Testing/Snippet/Parser/Snippet.php index 400aa64695da..6653d8e5838b 100644 --- a/Core/src/Testing/Snippet/Parser/Snippet.php +++ b/Core/src/Testing/Snippet/Parser/Snippet.php @@ -168,6 +168,7 @@ public function invoke($returnVar = null) $out = ob_get_clean(); } catch (\Exception $e) { ob_end_clean(); + var_dump($content); throw $e; } diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index fd210a3ca877..2af0e9d38585 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -21,6 +21,8 @@ use DateTimeInterface; use Google\ApiCore\OperationResponse; use Google\Cloud\Core\Serializer; +use Google\Cloud\Core\Iterator\ItemIterator; +use Google\Cloud\Core\Iterator\PageIterator; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; @@ -33,6 +35,8 @@ use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; +use Google\LongRunning\ListOperationsRequest; +use Google\LongRunning\Operation; /** * Represents a Cloud Spanner Backup. @@ -352,14 +356,70 @@ public function updateExpireTime(DateTimeInterface $newTimestamp, array $options * @param string $operationName The Long Running Operation name. * @return OperationResponse */ - public function resumeOperation($operationName) + public function resumeOperation($operationName, array $options = []) { return (new OperationResponse( $operationName, - $this->databaseAdminClient->getOperationsClient() + $this->databaseAdminClient->getOperationsClient(), + $options ))->withResultFunction($this->backupResultFunction()); } + /** + * List long running operations. + * + * Example: + * ``` + * $operations = $backup->longRunningOperations(); + * ``` + * + * @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 PagedListResponse<OperationResponse> + */ + public function longRunningOperations(array $options = []) + { + $options += ['name' => $this->name . '/operations']; + $resultLimit = $this->pluck('resultLimit', $options, false) ?: 0; + [$data, $callOptions] = $this->splitOptionalArgs($options); + $request = $this->serializer->decodeMessage(new ListOperationsRequest(), $data); + + return new ItemIterator( + new PageIterator( + function (Operation $operation) { + return $this->resumeOperation( + $operation->getName(), + ['lastProtoResponse' => $operation] + ); + }, + function (array $args) { + $nextPageToken = $this->pluck('pageToken', $args, false) ?: null; + $operationsClient = $this->databaseAdminClient->getOperationsClient(); + $page = $operationsClient->listOperations(...$args)->getPage(); + return [ + 'operations' => iterator_to_array($page->getResponseObject()->getOperations()), + 'nextResultTokenPath' => $page->getNextPageToken(), + ]; + }, + [$request, $callOptions], + [ + 'itemsKey' => 'operations', + 'resultLimit' => $resultLimit + ] + ) + ); + } + /** * Convert the simple backup name to a fully qualified name. * diff --git a/Spanner/tests/Snippet/ArrayTypeTest.php b/Spanner/tests/Snippet/ArrayTypeTest.php index 205e35f1f208..53c92a83cf8e 100644 --- a/Spanner/tests/Snippet/ArrayTypeTest.php +++ b/Spanner/tests/Snippet/ArrayTypeTest.php @@ -94,27 +94,21 @@ public function setUp(): void public function testConstructor() { - $field = [ - 'code' => Database::TYPE_ARRAY, - 'arrayElementType' => [ - 'code' => Database::TYPE_STRING - ] - ]; - $this->spannerClient->executeStreamingSql( - Argument::that(function ($args) use ($values, $field) { - $message = $this->serializer->encodeMessage($args); - $this->assertEquals($message['sql'], 'SELECT @arrayParam as arrayValue'); - $this->assertEquals($message['params'], ['arrayParam' => [ - 'foo', 'bar', null - ]]); + Argument::that(function ($request) { + $message = $this->serializer->encodeMessage($request); + $this->assertEquals('SELECT @arrayParam as arrayValue', $request->getSql()); + $this->assertEquals( + ['arrayParam' => ['foo', 'bar', null]], + $message['params'] + ); $this->assertEquals( + Database::TYPE_STRING, $message['paramTypes']['arrayParam']['arrayElementType']['code'], - $field['arrayElementType']['code'] ); $this->assertEquals( + Database::TYPE_ARRAY, $message['paramTypes']['arrayParam']['code'], - $field['code'] ); return true; }), @@ -128,7 +122,12 @@ public function testConstructor() 'fields' => [ [ 'name' => 'arrayValue', - 'type' => $field + 'type' => [ + 'code' => Database::TYPE_ARRAY, + 'arrayElementType' => [ + 'code' => Database::TYPE_STRING + ] + ] ] ] ] diff --git a/Spanner/tests/Snippet/BackupTest.php b/Spanner/tests/Snippet/BackupTest.php index 570f9939cc46..0ee5a7d59f65 100644 --- a/Spanner/tests/Snippet/BackupTest.php +++ b/Spanner/tests/Snippet/BackupTest.php @@ -18,13 +18,29 @@ namespace Google\Cloud\Spanner\Tests\Snippet; use Google\ApiCore\OperationResponse; +use Google\ApiCore\Page; +use Google\ApiCore\PagedListResponse; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; +use Google\Cloud\Core\Serializer; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Backup; +use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\SpannerClient; +use Google\LongRunning\Client\OperationsClient; +use Google\LongRunning\ListOperationsRequest; +use Google\LongRunning\ListOperationsResponse; +use Google\LongRunning\Operation; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -42,10 +58,9 @@ class BackupTest extends SnippetTestCase const DATABASE = 'my-database'; const BACKUP = 'my-backup'; - private $spannerClient; private $serializer; private $backup; - private $client; + private $spanner; private $instance; private $expireTime; @@ -53,23 +68,21 @@ public function setUp(): void { $this->checkAndSkipGrpcTests(); + $this->databaseAdminClient = $this->prophesize(DatabaseAdminClient::class); + $this->serializer = new Serializer(); - $this->client = new SpannerClient( - [['projectId' => 'my-project']], - ['requestHandler', 'serializer'] - ); + $this->spanner = new SpannerClient(['projectId' => 'my-project']); $this->expireTime = new \DateTime('+ 7 hours'); - $this->instance = new Instance( - $this->requestHandler->reveal(), - $this->serializer, - self::PROJECT, - self::INSTANCE - ); + $database = $this->prophesize(Database::class); + $database->name()->willReturn(DatabaseAdminClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE)); + $instance = $this->prophesize(Instance::class); + $instance->name()->willReturn(InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)); + $instance->database('my-database')->willReturn($database->reveal()); $this->backup = new Backup( - $this->requestHandler->reveal(), + $this->databaseAdminClient->reveal(), $this->serializer, - $this->instance, + $instance->reveal(), self::PROJECT, self::BACKUP ); @@ -92,53 +105,45 @@ public function testCreate() $snippet = $this->snippetFromMethod(Backup::class, 'create'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'createBackup', - null, - $this->getOperationResponseMock() - ); - - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); + $this->databaseAdminClient->createBackup( + Argument::type(CreateBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $res = $snippet->invoke('operation'); - $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); + // $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } - public function testCreateCopy() - { - $snippet = $this->snippetFromMethod(Backup::class, 'createCopy'); - $snippet->addLocal('spanner', $this->client); - - $this->mockSendRequest( - DatabaseAdminClient::class, - 'copyBackup', - null, - $this->getOperationResponseMock() - ); + // public function testCreateCopy() + // { + // $snippet = $this->snippetFromMethod(Backup::class, 'createCopy'); + // $snippet->addLocal('spanner', $this->spanner); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); + // $this->databaseAdminClient->copyBackup( + // Argument::type(CopyBackupRequest::class), + // Argument::type('array') + // ) + // ->shouldBeCalledOnce() + // ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $res = $snippet->invoke('operation'); - $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); - } + + // $res = $snippet->invoke('operation'); + // $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); + // } public function testDelete() { $snippet = $this->snippetFromMethod(Backup::class, 'delete'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'deleteBackup', - null, - null - ); + $this->databaseAdminClient->deleteBackup( + Argument::type(DeleteBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce(); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -148,15 +153,12 @@ public function testExists() $snippet = $this->snippetFromMethod(Backup::class, 'exists'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getBackup', - null, - ['foo' => 'bar'] - ); - - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new BackupProto()); $res = $snippet->invoke(); $this->assertEquals('Backup exists!', $res->output()); @@ -169,18 +171,16 @@ public function testInfo() $snippet = $this->snippetFromMethod(Backup::class, 'info'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getBackup', - null, - $backup - ); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new BackupProto($backup)); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); - $this->assertEquals($backup, $res->returnVal()); + $this->assertEquals($backup['name'], $res->returnVal()['name']); $snippet->invoke(); } @@ -198,24 +198,21 @@ public function testName() public function testReload() { - $bkp = ['name' => 'foo']; + $backup = ['name' => 'foo']; $snippet = $this->snippetFromMethod(Backup::class, 'reload'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getBackup', - null, - $bkp, - 2 - ); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledTimes(2) + ->willReturn(new BackupProto($backup)); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); - $this->assertEquals($bkp, $res->returnVal()); + $this->assertEquals($backup['name'], $res->returnVal()['name']); $snippet->invoke(); } @@ -224,15 +221,13 @@ public function testState() $snippet = $this->snippetFromMethod(Backup::class, 'state'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getBackup', - null, - ['state' => Backup::STATE_READY] - ); + $this->databaseAdminClient->getBackup( + Argument::type(GetBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new BackupProto(['state' => Backup::STATE_READY])); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Backup is ready!', $res->output()); @@ -240,34 +235,40 @@ public function testState() public function testUpdateExpireTime() { - $bkp = ['name' => 'foo', 'expireTime' => $this->expireTime->format('Y-m-d\TH:i:s.u\Z')]; + $backup = [ + 'name' => 'foo', + 'expire_time' => new \Google\Protobuf\Timestamp(['seconds' => $this->expireTime->format('U')]) + ]; $snippet = $this->snippetFromMethod(Backup::class, 'updateExpireTime'); $snippet->addLocal('backup', $this->backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'updateBackup', - null, - $bkp - ); + $this->databaseAdminClient->updateBackup( + Argument::type(UpdateBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new BackupProto($backup)); - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); - $this->assertEquals($bkp, $res->returnVal()); + $this->assertEquals($backup['name'], $res->returnVal()['name']); + $this->assertEquals( + $this->expireTime->format('Y-m-d\TH:i:s.000000\Z'), + $res->returnVal()['expireTime'] + ); } public function testResumeOperation() { $snippet = $this->snippetFromMagicMethod(Backup::class, 'resumeOperation'); + $snippet->addLocal('spanner', $this->spanner); $snippet->addLocal('backup', $this->backup); $snippet->addLocal('operationName', 'foo'); $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); - $this->assertEquals('foo', $res->returnVal()->name()); + $this->assertEquals('foo', $res->returnVal()->getName()); } public function testLongRunningOperations() @@ -275,19 +276,27 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Backup::class, 'longRunningOperations'); $snippet->addLocal('backup', $this->backup); - $this->requestHandler - ->getClientObject(Argument::any()) - ->willReturn(new DatabaseAdminClient()); - $this->requestHandler - ->sendRequest( - Argument::any(), - 'listOperations', - Argument::cetera() - ) - ->willReturn([$this->getOperationResponseMock()]); - - $this->backup->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->backup->___setProperty('serializer', $this->serializer); + $operation = new Operation(); + + $page = $this->prophesize(Page::class); + $page->getResponseObject() + ->willReturn(new ListOperationsResponse(['operations' => [$operation]])); + $page->getNextPageToken() + ->willReturn(null); + $pagedListResponse = $this->prophesize(PagedListResponse::class); + $pagedListResponse->getPage() + ->willReturn($page->reveal()); + + $operationsClient = $this->prophesize(OperationsClient::class); + $operationsClient->listOperations( + Argument::type(ListOperationsRequest::class), + Argument::type('array') + ) + ->willReturn($pagedListResponse->reveal()); + + $this->databaseAdminClient->getOperationsClient() + ->shouldBeCalled() + ->willReturn($operationsClient->reveal()); $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); diff --git a/Spanner/tests/Snippet/Batch/BatchClientTest.php b/Spanner/tests/Snippet/Batch/BatchClientTest.php index 9d034ee56b71..f0de9167aa35 100644 --- a/Spanner/tests/Snippet/Batch/BatchClientTest.php +++ b/Spanner/tests/Snippet/Batch/BatchClientTest.php @@ -136,7 +136,6 @@ public function testPubSubExample() ] ]); - $pubsub->___setProperty('requestHandler', $requestHandler->reveal()); // setup spanner service call stubs $this->spannerClient->partitionQuery( diff --git a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php index ce9e8fd6b78b..bbce2e7c91d6 100644 --- a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php @@ -128,7 +128,6 @@ public function testClose() $this->session->delete([]) ->shouldBeCalled(); - $this->snapshot->___setProperty('session', $this->session->reveal()); $snippet = $this->snippetFromMethod(BatchSnapshot::class, 'close'); $snippet->addLocal('snapshot', $this->snapshot); diff --git a/Spanner/tests/Snippet/CommitTimestampTest.php b/Spanner/tests/Snippet/CommitTimestampTest.php index ac1d16c48fe9..98bc87449e33 100644 --- a/Spanner/tests/Snippet/CommitTimestampTest.php +++ b/Spanner/tests/Snippet/CommitTimestampTest.php @@ -23,6 +23,9 @@ use Google\Cloud\Spanner\CommitTimestamp; use Google\Cloud\Spanner\SpannerClient; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\Cloud\Spanner\V1\CreateSessionRequest; +use Google\Cloud\Spanner\V1\DeleteSessionRequest; +use Google\Cloud\Spanner\V1\CommitRequest; /** * @group spanner @@ -52,18 +55,17 @@ public function testClass() ['requestHandler', 'serializer'] ); - $this->mockSendRequest( - GapicSpannerClient::class, - 'createSession', - null, - ['name' => self::SESSION] - ); - $this->mockSendRequest( - GapicSpannerClient::class, - 'deleteSession', - null, - null - ); + $this->GapicSpannerClient->createSession( + Argument::type(CreateSessionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CreateSessionResponse(['name' => self::SESSION])); + $this->GapicSpannerClient->deleteSession( + Argument::type(DeleteSessionRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce(); $mutation = [ 'insert' => [ 'table' => 'myTable', @@ -71,10 +73,12 @@ public function testClass() 'values' => [[$id, CommitTimestamp::SPECIAL_VALUE]] ] ]; - $this->mockSendRequest( - GapicSpannerClient::class, - 'commit', - function ($args) use ($mutation) { + $this->GapicSpannerClient->commit( + Argument::type(CommitRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(function ($args) use ($mutation) { $message = $this->serializer->encodeMessage($args); $this->assertEquals($message['mutations'][0], $mutation); return true; @@ -84,8 +88,6 @@ function ($args) use ($mutation) { ] ); - $client->___setProperty('requestHandler', $this->requestHandler->reveal()); - $client->___setProperty('serializer', $this->serializer); $snippet = $this->snippetFromClass(CommitTimestamp::class); $snippet->addLocal('id', $id); diff --git a/Spanner/tests/Snippet/DatabaseTest.php b/Spanner/tests/Snippet/DatabaseTest.php index c53f687c92d6..18ab6970b552 100644 --- a/Spanner/tests/Snippet/DatabaseTest.php +++ b/Spanner/tests/Snippet/DatabaseTest.php @@ -24,6 +24,15 @@ use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\DropDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseDdlRequest; +use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesRequest; +use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseDdlRequest; +use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; @@ -128,15 +137,13 @@ public function testState() $snippet->addLocal('database', $this->database); $snippet->addUse(Database::class); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getDatabase', - null, - ['state' => Database::STATE_READY] - ); + $this->databaseAdminClient->getDatabase( + Argument::type(GetDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseResponse(['state' => Database::STATE_READY])); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Database is ready!', $res->output()); @@ -150,11 +157,12 @@ public function testBackups() $snippet = $this->snippetFromMethod(Database::class, 'backups'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'listBackups', - null, - [ + $this->databaseAdminClient->listBackups( + Argument::type(ListBackupsRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'backups' => [ [ 'name' => DatabaseAdminClient::backupName(self::PROJECT, self::INSTANCE, self::BACKUP) @@ -163,7 +171,6 @@ public function testBackups() ] ); - $this->instance->___setProperty('requestHandler', $this->requestHandler->reveal()); $res = $snippet->invoke('backups'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); @@ -178,15 +185,13 @@ public function testCreateBackup() $snippet = $this->snippetFromMethod(Database::class, 'createBackup'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'createBackup', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->createBackup( + Argument::type(CreateBackupRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); } @@ -207,15 +212,13 @@ public function testExists() $snippet = $this->snippetFromMethod(Database::class, 'exists'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getDatabase', - null, - ['statements' => []] - ); + $this->databaseAdminClient->getDatabase( + Argument::type(GetDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseResponse(['statements' => []])); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke(); $this->assertEquals('Database exists!', $res->output()); @@ -231,15 +234,13 @@ public function testInfo() $snippet = $this->snippetFromMethod(Database::class, 'info'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getDatabase', - null, - $db - ); + $this->databaseAdminClient->getDatabase( + Argument::type(GetDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseResponse($db)); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($db, $res->returnVal()); @@ -256,16 +257,15 @@ public function testReload() $snippet = $this->snippetFromMethod(Database::class, 'reload'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getDatabase', - null, - $db, + $this->databaseAdminClient->getDatabase( + Argument::type(GetDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($db, 2 ); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('info'); $this->assertEquals($db, $res->returnVal()); @@ -280,15 +280,13 @@ public function testCreate() $snippet = $this->snippetFromMethod(Database::class, 'create'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'createDatabase', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->createDatabase( + Argument::type(CreateDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); @@ -304,15 +302,13 @@ public function testRestore() $snippet->addLocal('database', $this->database); $snippet->addLocal('backup', $backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'restoreDatabase', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->restoreDatabase( + Argument::type(RestoreDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); @@ -326,15 +322,13 @@ public function testUpdateDdl() $snippet = $this->snippetFromMethod(Database::class, 'updateDdl'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'updateDatabaseDdl', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->updateDatabaseDdl( + Argument::type(UpdateDatabaseDdlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -347,15 +341,13 @@ public function testUpdateDdlBatch() $snippet = $this->snippetFromMethod(Database::class, 'updateDdlBatch'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'updateDatabaseDdl', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->updateDatabaseDdl( + Argument::type(UpdateDatabaseDdlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -368,15 +360,12 @@ public function testDrop() $snippet = $this->snippetFromMethod(Database::class, 'drop'); $snippet->addLocal('database', $this->database); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'dropDatabase', - null, - null - ); + $this->databaseAdminClient->dropDatabase( + Argument::type(DropDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce(); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $snippet->invoke(); } @@ -394,15 +383,13 @@ public function testDdl() 'CREATE TABLE TestCases' ]; - $this->mockSendRequest( - DatabaseAdminClient::class, - 'getDatabaseDdl', - null, - ['statements' => $stmts] - ); + $this->databaseAdminClient->getDatabaseDdl( + Argument::type(GetDatabaseDdlRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetDatabaseDdlResponse(['statements' => $stmts])); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('statements'); $this->assertEquals($stmts, $res->returnVal()); @@ -954,9 +941,6 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Database::class, 'longRunningOperations'); $snippet->addLocal('database', $this->database); - $this->requestHandler - ->getClientObject(Argument::any()) - ->willReturn(new DatabaseAdminClient()); $this->requestHandler ->sendRequest( Argument::any(), @@ -965,8 +949,6 @@ public function testLongRunningOperations() ) ->willReturn([$this->getOperationResponseMock()]); - $this->database->___setProperty('requestHandler', $this->requestHandler->reveal()); - $this->database->___setProperty('serializer', $this->serializer); $res = $snippet->invoke('operations'); $this->assertInstanceOf(ItemIterator::class, $res->returnVal()); diff --git a/Spanner/tests/Snippet/InstanceConfigurationTest.php b/Spanner/tests/Snippet/InstanceConfigurationTest.php index bc73f1a004b1..d2095a60b091 100644 --- a/Spanner/tests/Snippet/InstanceConfigurationTest.php +++ b/Spanner/tests/Snippet/InstanceConfigurationTest.php @@ -22,6 +22,9 @@ use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceConfigRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; use Google\Cloud\Spanner\InstanceConfiguration; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -73,12 +76,12 @@ public function testClass() public function testCreate() { $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'create'); - $this->mockSendRequest( - InstanceAdminClient::class, - 'createInstanceConfig', - null, - $this->getOperationResponseMock(), - ); + $this->instanceAdminClient->createInstanceConfig( + Argument::type(CreateInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CreateInstanceConfigResponse($this->getOperationResponseMock(),)); $baseConfig = new InstanceConfiguration( $this->requestHandler->reveal(), @@ -108,12 +111,12 @@ public function testUpdate() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'update'); $snippet->addLocal('instanceConfig', $this->config); - $this->mockSendRequest( - InstanceAdminClient::class, - 'updateInstanceConfig', - null, - $this->getOperationResponseMock() - ); + $this->instanceAdminClient->updateInstanceConfig( + Argument::type(UpdateInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $this->config->___setProperty('requestHandler', $this->requestHandler->reveal()); $this->config->___setProperty('serializer', $this->serializer); @@ -166,11 +169,12 @@ public function testExists() $snippet = $this->snippetFromMethod(InstanceConfiguration::class, 'exists'); $snippet->addLocal('configuration', $this->config); - $this->mockSendRequest( - InstanceAdminClient::class, - 'getInstanceConfig', - null, - [ + $this->instanceAdminClient->getInstanceConfig( + Argument::type(GetInstanceConfigRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'name' => InstanceAdminClient::instanceConfigName(self::PROJECT, self::CONFIG), 'displayName' => self::CONFIG ] diff --git a/Spanner/tests/Snippet/InstanceTest.php b/Spanner/tests/Snippet/InstanceTest.php index b0ac6e52b56d..6f9dd620ecd2 100644 --- a/Spanner/tests/Snippet/InstanceTest.php +++ b/Spanner/tests/Snippet/InstanceTest.php @@ -22,8 +22,16 @@ use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; +use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabasesRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListBackupOperationsRequest; +use Google\Cloud\Spanner\Admin\Database\V1\ListDatabaseOperationsRequest; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\GetInstanceRequest; use Google\Cloud\Spanner\Backup; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; @@ -87,12 +95,12 @@ public function testCreate() $snippet->addLocal('configuration', $config->reveal()); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - InstanceAdminClient::class, - 'createInstance', - null, - $this->getOperationResponseMock() - ); + $this->instanceAdminClient->createInstance( + Argument::type(CreateInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $this->instance->___setProperty( 'requestHandler', @@ -116,12 +124,12 @@ public function testInfo() $snippet = $this->snippetFromMethod(Instance::class, 'info'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - InstanceAdminClient::class, - 'getInstance', - null, - ['nodeCount' => 1] - ); + $this->instanceAdminClient->getInstance( + Argument::type(GetInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetInstanceResponse(['nodeCount' => 1])); $this->instance->___setProperty( 'requestHandler', @@ -136,12 +144,12 @@ public function testExists() $snippet = $this->snippetFromMethod(Instance::class, 'exists'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - InstanceAdminClient::class, - 'getInstance', - null, - ['foo' => 'bar'] - ); + $this->instanceAdminClient->getInstance( + Argument::type(GetInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetInstanceResponse(['foo' => 'bar'])); $this->instance->___setProperty( 'requestHandler', @@ -156,12 +164,12 @@ public function testReload() $snippet = $this->snippetFromMethod(Instance::class, 'reload'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - InstanceAdminClient::class, - 'getInstance', - null, - ['nodeCount' => 1] - ); + $this->instanceAdminClient->getInstance( + Argument::type(GetInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetInstanceResponse(['nodeCount' => 1])); $this->instance->___setProperty( 'requestHandler', @@ -178,12 +186,12 @@ public function testState() $snippet->addLocal('instance', $this->instance); $snippet->addUse(Instance::class); - $this->mockSendRequest( - InstanceAdminClient::class, - 'getInstance', - null, - ['state' => Instance::STATE_READY] - ); + $this->instanceAdminClient->getInstance( + Argument::type(GetInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new GetInstanceResponse(['state' => Instance::STATE_READY])); $this->instance->___setProperty( 'requestHandler', @@ -198,12 +206,12 @@ public function testUpdate() $snippet = $this->snippetFromMethod(Instance::class, 'update'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - InstanceAdminClient::class, - 'updateInstance', - null, - $this->getOperationResponseMock() - ); + $this->instanceAdminClient->updateInstance( + Argument::type(UpdateInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $this->instance->___setProperty( 'requestHandler', @@ -231,12 +239,12 @@ public function testCreateDatabase() $snippet = $this->snippetFromMethod(Instance::class, 'createDatabase'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'createDatabase', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->createDatabase( + Argument::type(CreateDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $this->instance->___setProperty( 'requestHandler', @@ -253,12 +261,12 @@ public function testCreateDatabaseFromBackup() $snippet->addLocal('instance', $this->instance); $snippet->addLocal('backup', $backup); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'restoreDatabase', - null, - $this->getOperationResponseMock() - ); + $this->databaseAdminClient->restoreDatabase( + Argument::type(RestoreDatabaseRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $this->instance->___setProperty( 'requestHandler', @@ -283,11 +291,12 @@ public function testDatabases() $snippet = $this->snippetFromMethod(Instance::class, 'databases'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'listDatabases', - null, - [ + $this->databaseAdminClient->listDatabases( + Argument::type(ListDatabasesRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'databases' => [ [ 'name' => DatabaseAdminClient::databaseName( @@ -325,11 +334,12 @@ public function testBackups() $snippet = $this->snippetFromMethod(Instance::class, 'backups'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'listBackups', - null, - [ + $this->databaseAdminClient->listBackups( + Argument::type(ListBackupsRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'backups' => [ [ 'name' => DatabaseAdminClient::backupName( @@ -363,11 +373,12 @@ public function testBackupOperations() $snippet = $this->snippetFromMethod(Instance::class, 'backupOperations'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'listBackupOperations', - null, - [ + $this->databaseAdminClient->listBackupOperations( + Argument::type(ListBackupOperationsRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'operations' => [ [ 'name' => $backupOperationName @@ -397,11 +408,12 @@ public function testDatabaseOperations() $snippet = $this->snippetFromMethod(Instance::class, 'databaseOperations'); $snippet->addLocal('instance', $this->instance); - $this->mockSendRequest( - DatabaseAdminClient::class, - 'listDatabaseOperations', - null, - [ + $this->databaseAdminClient->listDatabaseOperations( + Argument::type(ListDatabaseOperationsRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'operations' => [ [ 'name' => $databaseOperationName @@ -445,9 +457,6 @@ public function testLongRunningOperations() $snippet = $this->snippetFromMethod(Instance::class, 'longRunningOperations'); $snippet->addLocal('instance', $this->instance); - $this->requestHandler - ->getClientObject(Argument::any()) - ->willReturn(new DatabaseAdminClient()); $this->requestHandler ->sendRequest( Argument::any(), diff --git a/Spanner/tests/Snippet/SpannerClientTest.php b/Spanner/tests/Snippet/SpannerClientTest.php index d67616e81bc6..8e3b6e76cb78 100644 --- a/Spanner/tests/Snippet/SpannerClientTest.php +++ b/Spanner/tests/Snippet/SpannerClientTest.php @@ -23,6 +23,8 @@ use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsRequest; +use Google\Cloud\Spanner\Admin\Instance\V1\CreateInstanceRequest; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\CommitTimestamp; @@ -86,11 +88,12 @@ public function testBatch() */ public function testInstanceConfigurations() { - $this->mockSendRequest( - InstanceAdminClient::class, - 'listInstanceConfigs', - null, - [ + $this->instanceAdminClient->listInstanceConfigs( + Argument::type(ListInstanceConfigsRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'instanceConfigs' => [ ['name' => 'projects/my-awesome-projects/instanceConfigs/foo'], ['name' => 'projects/my-awesome-projects/instanceConfigs/bar'], @@ -136,12 +139,12 @@ public function testCreateInstance() $snippet->addLocal('spanner', $this->client); $snippet->addLocal('configuration', $this->client->instanceConfiguration(self::CONFIG)); - $this->mockSendRequest( - InstanceAdminClient::class, - 'createInstance', - null, - $this->getOperationResponseMock() - ); + $this->instanceAdminClient->createInstance( + Argument::type(CreateInstanceRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->prophesize(OperationResponse::class)->reveal()); $res = $snippet->invoke('operation'); $this->assertInstanceOf(OperationResponse::class, $res->returnVal()); @@ -171,11 +174,12 @@ public function testInstances() $snippet = $this->snippetFromMethod(SpannerClient::class, 'instances'); $snippet->addLocal('spanner', $this->client); - $this->mockSendRequest( - InstanceAdminClient::class, - 'listInstances', - null, - [ + $this->instanceAdminClient->listInstances( + Argument::type(ListInstancesRequest::class), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn([ 'instances' => [ ['name' => InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)], ['name' => InstanceAdminClient::instanceName(self::PROJECT, 'bar')] diff --git a/Spanner/tests/Snippet/TransactionTest.php b/Spanner/tests/Snippet/TransactionTest.php index 7b8c7cff2fd2..31b747eae17e 100644 --- a/Spanner/tests/Snippet/TransactionTest.php +++ b/Spanner/tests/Snippet/TransactionTest.php @@ -497,7 +497,6 @@ public function testIsRetry() $snippet = $this->snippetFromMethod(Transaction::class, 'isRetry'); $snippet->addLocal('transaction', $this->transaction); - $this->transaction->___setProperty('isRetry', true); $res = $snippet->invoke(); $this->assertEquals('This is a retry transaction!', $res->output()); From f3872d11a822c0aed861133235beaf8f2804d778 Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Thu, 14 Nov 2024 12:10:40 -0800 Subject: [PATCH 7/8] trait and serializer cleanup --- Core/src/Serializer.php | 46 --------- Spanner/src/Backup.php | 3 - Spanner/src/Database.php | 4 - Spanner/src/FormatKeySetTrait.php | 37 ------- Spanner/src/Instance.php | 4 - Spanner/src/InstanceConfiguration.php | 3 - Spanner/src/Operation.php | 23 ++--- Spanner/src/RequestTrait.php | 5 + Spanner/src/Serializer.php | 99 +++++++++++++++++++ Spanner/src/Session/Session.php | 4 +- Spanner/src/SpannerClient.php | 29 +++++- Spanner/tests/Snippet/ArrayTypeTest.php | 2 +- Spanner/tests/Snippet/BackupTest.php | 2 +- .../tests/Snippet/Batch/BatchClientTest.php | 2 +- Spanner/tests/Unit/BackupTest.php | 2 +- Spanner/tests/Unit/Batch/BatchClientTest.php | 2 +- .../tests/Unit/Batch/BatchSnapshotTest.php | 27 ++++- Spanner/tests/Unit/DatabaseTest.php | 2 +- .../tests/Unit/InstanceConfigurationTest.php | 2 +- Spanner/tests/Unit/InstanceTest.php | 2 +- Spanner/tests/Unit/OperationTest.php | 2 +- Spanner/tests/Unit/SpannerClientTest.php | 2 +- Spanner/tests/Unit/TransactionTest.php | 2 +- Spanner/tests/Unit/TransactionTypeTest.php | 2 +- 24 files changed, 172 insertions(+), 136 deletions(-) delete mode 100644 Core/src/Serializer.php delete mode 100644 Spanner/src/FormatKeySetTrait.php create mode 100644 Spanner/src/Serializer.php diff --git a/Core/src/Serializer.php b/Core/src/Serializer.php deleted file mode 100644 index e8e194b93c3f..000000000000 --- a/Core/src/Serializer.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -namespace Google\Cloud\Core; - -use Google\ApiCore\Serializer as ApiCoreSerializer; -use Google\Cloud\Core\ApiHelperTrait; - -/** - * @internal - * Supplies helper methods to interact with the APIs. - */ -class Serializer extends ApiCoreSerializer -{ - use ApiHelperTrait; - - public function __construct( - $fieldTransformers = [], - $messageTypeTransformers = [], - $decodeFieldTransformers = [], - $decodeMessageTypeTransformers = [], - $customEncoders = [], - ) { - $messageTypeTransformers += [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]; - - parent::__construct( - $fieldTransformers, - $messageTypeTransformers, - $decodeFieldTransformers, - $decodeMessageTypeTransformers, - $customEncoders, - ); - } -} \ No newline at end of file diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index 2af0e9d38585..fb37ef528cfd 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -20,7 +20,6 @@ use Closure; use DateTimeInterface; use Google\ApiCore\OperationResponse; -use Google\Cloud\Core\Serializer; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Iterator\PageIterator; use Google\ApiCore\ValidationException; @@ -52,8 +51,6 @@ */ class Backup { - use ApiHelperTrait; - use RequestProcessorTrait; use RequestTrait; const STATE_READY = State::READY; diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index c366c8e55878..b2be249343d6 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -21,7 +21,6 @@ use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; use Google\ApiCore\RetrySettings; -use Google\Cloud\Core\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\AbortedException; @@ -89,9 +88,6 @@ class Database { use TransactionConfigurationTrait; use RequestTrait; - use RequestProcessorTrait; - use ApiHelperTrait; - use FormatKeySetTrait; const STATE_CREATING = State::CREATING; const STATE_READY = State::READY; diff --git a/Spanner/src/FormatKeySetTrait.php b/Spanner/src/FormatKeySetTrait.php deleted file mode 100644 index de79d008f21f..000000000000 --- a/Spanner/src/FormatKeySetTrait.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -namespace Google\Cloud\Spanner; - -use Google\Cloud\Core\ApiHelperTrait; - -trait FormatKeySetTrait -{ - use ApiHelperTrait; - - /** - * @param array $keySet - * @return array Formatted keyset - */ - private function formatKeySet(array $keySet) - { - $keys = $this->pluck('keys', $keySet, false); - if ($keys) { - $keySet['keys'] = array_map( - fn ($key) => $this->formatListForApi((array) $key), - $keys - ); - } - - if (isset($keySet['ranges'])) { - $keySet['ranges'] = array_map(function ($rangeItem) { - return array_map([$this, 'formatListForApi'], $rangeItem); - }, $keySet['ranges']); - - if (empty($keySet['ranges'])) { - unset($keySet['ranges']); - } - } - - return $keySet; - } -} diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index d0c50af270b7..8dce44413d35 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -20,7 +20,6 @@ use Closure; use Google\ApiCore\ArrayTrait; use Google\ApiCore\OperationResponse; -use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; @@ -55,10 +54,7 @@ */ class Instance { - use ApiHelperTrait; - use ArrayTrait; use RequestTrait; - use RequestProcessorTrait; const STATE_READY = State::READY; const STATE_CREATING = State::CREATING; diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index 002bbe8a39fe..fc54c16156ae 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -20,7 +20,6 @@ use Closure; use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; -use Google\Cloud\Core\Serializer; use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\RequestProcessorTrait; @@ -52,9 +51,7 @@ */ class InstanceConfiguration { - use ApiHelperTrait; use RequestTrait; - use RequestProcessorTrait; /** * @var string diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index f83c6321c478..64e218234b38 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -17,12 +17,6 @@ namespace Google\Cloud\Spanner; -use Google\ApiCore\ArrayTrait; -use Google\Cloud\Core\Serializer; -use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\RequestProcessorTrait; -use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\Session\Session; @@ -53,14 +47,8 @@ */ class Operation { - use ApiHelperTrait; - use ArrayTrait; use RequestTrait; - use RequestProcessorTrait; use MutationTrait; - use TimeTrait; - use ValidateTrait; - use FormatKeySetTrait; const OP_INSERT = 'insert'; const OP_UPDATE = 'update'; @@ -813,7 +801,8 @@ public function partitionRead( 'session' => $session->name(), 'table' => $table, 'columns' => $columns, - 'keySet' => $this->formatKeySet($this->flattenKeySet($keySet)), + // 'keySet' => $this->formatKeySet($this->flattenKeySet($keySet)), + 'keySet' => $this->flattenKeySet($keySet), 'partitionOptions' => $this->partitionOptions($data) ]; @@ -932,9 +921,9 @@ private function serializeMutations(array $mutations) switch ($type) { case Operation::OP_DELETE: - if (isset($data['keySet'])) { - $data['keySet'] = $this->formatKeySet($data['keySet']); - } + // if (isset($data['keySet'])) { + // $data['keySet'] = $this->formatKeySet($data['keySet']); + // } break; default: $modifiedData = array_map([$this, 'formatValueForApi'], $data['values']); @@ -1088,7 +1077,7 @@ private function executeStreamingSql(array $args) private function streamingRead(array $args) { list($data, $callOptions) = $this->splitOptionalArgs($args); - $data['keySet'] = $this->formatKeySet($this->pluck('keySet', $data)); + // $data['keySet'] = ($this->pluck('keySet', $data); $data['transaction'] = $this->createTransactionSelector($data); $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); $databaseName = $this->pluck('database', $data); diff --git a/Spanner/src/RequestTrait.php b/Spanner/src/RequestTrait.php index 9b0f5e0b795a..663fad17c158 100644 --- a/Spanner/src/RequestTrait.php +++ b/Spanner/src/RequestTrait.php @@ -18,6 +18,8 @@ namespace Google\Cloud\Spanner; use Google\Cloud\Spanner\Session\SessionPoolInterface; +use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\RequestProcessorTrait; /** * Shared functionality for Spanner requests. @@ -26,6 +28,9 @@ */ trait RequestTrait { + use ApiHelperTrait; + use RequestProcessorTrait; + private $larHeader = 'x-goog-spanner-route-to-leader'; private $resourcePrefixHeader = 'google-cloud-resource-prefix'; diff --git a/Spanner/src/Serializer.php b/Spanner/src/Serializer.php new file mode 100644 index 000000000000..4b1ef0dd3f78 --- /dev/null +++ b/Spanner/src/Serializer.php @@ -0,0 +1,99 @@ +<?php + +namespace Google\Cloud\Spanner; + +use Google\ApiCore\Serializer as ApiCoreSerializer; +use Google\Cloud\Core\ApiHelperTrait; + +/** + * @internal + * Supplies helper methods to interact with the APIs. + */ +class Serializer extends ApiCoreSerializer +{ + use ApiHelperTrait; + + public function __construct() + { + $fieldTransformers = []; + $messageTypeTransformers = [ + 'google.protobuf.Value' => function ($v) { + return $this->flattenValue($v); + }, + 'google.protobuf.ListValue' => function ($v) { + return $this->flattenListValue($v); + }, + 'google.protobuf.Struct' => function ($v) { + return $this->flattenStruct($v); + }, + 'google.protobuf.Timestamp' => function ($v) { + return $this->formatTimestampFromApi($v); + } + ]; + $decodeFieldTransformers = []; + $decodeMessageTypeTransformers = [ + 'google.spanner.v1.KeySet' => function ($keySet) { + $keys = $this->pluck('keys', $keySet, false); + if ($keys) { + $keySet['keys'] = array_map( + fn ($key) => $this->formatListForApi((array) $key), + $keys + ); + } + + if (isset($keySet['ranges'])) { + $keySet['ranges'] = array_map(function ($rangeItem) { + return array_map([$this, 'formatListForApi'], $rangeItem); + }, $keySet['ranges']); + + if (empty($keySet['ranges'])) { + unset($keySet['ranges']); + } + } + + return $keySet; + } + ]; + $customEncoders = [ + // A custom encoder that short-circuits the encodeMessage in Serializer class, + // but only if the argument is of the type PartialResultSet. + PartialResultSet::class => function ($msg) { + $data = json_decode($msg->serializeToJsonString(), true); + + // We only override metadata fields, if it actually exists in the response. + // This is specially important for large data sets which is received in chunks. + // Metadata is only received in the first 'chunk' and we don't want to set empty metadata fields + // when metadata was not returned from the server. + if (isset($data['metadata'])) { + // The transaction id is serialized as a base64 encoded string in $data. So, we + // add a step to get the transaction id using a getter instead of the serialized value. + // The null-safe operator is used to handle edge cases where the relevant fields are not present. + $data['metadata']['transaction']['id'] = (string) $msg?->getMetadata()?->getTransaction()?->getId(); + + // Helps convert metadata enum values from string types to their respective code/annotation + // pairs. Ex: INT64 is converted to {code: 2, typeAnnotation: 0}. + $fields = $msg->getMetadata()?->getRowType()?->getFields(); + $data['metadata']['rowType']['fields'] = $this->getFieldDataFromRepeatedFields($fields); + } + + // These fields in stats should be an int + if (isset($data['stats']['rowCountLowerBound'])) { + $data['stats']['rowCountLowerBound'] = (int) $data['stats']['rowCountLowerBound']; + } + if (isset($data['stats']['rowCountExact'])) { + $data['stats']['rowCountExact'] = (int) $data['stats']['rowCountExact']; + } + + return $data; + } + ]; + + parent::__construct( + $fieldTransformers, + $messageTypeTransformers, + $decodeFieldTransformers, + $decodeMessageTypeTransformers, + $customEncoders, + ); + } +} \ No newline at end of file diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index ead94f85870f..fefed790e390 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner\Session; use Google\ApiCore\ArrayTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Spanner\Database; @@ -32,8 +32,6 @@ */ class Session { - use ApiHelperTrait; - use ArrayTrait; use RequestTrait; /** diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index db83ef5efad0..8883c8a4ec88 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -32,7 +32,6 @@ use Google\Cloud\Core\Iterator\PageIterator; use Google\Cloud\Core\Middleware\ExceptionMiddleware; use Google\Cloud\Core\RequestProcessorTrait; -use Google\Cloud\Core\Serializer; use Google\Cloud\Core\ValidateTrait; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; @@ -108,13 +107,10 @@ */ class SpannerClient { - use ApiHelperTrait; use ClientOptionsTrait; use ClientTrait; use EmulatorTrait; - use ValidateTrait; use RequestTrait; - use RequestProcessorTrait; const VERSION = '1.89.0'; @@ -255,7 +251,30 @@ public function __construct(array $config = []) ); } $this->projectId = $this->detectProjectId($config); - $this->serializer = new Serializer([], [], [], [], [ + $this->serializer = new Serializer([], [ + 'google.spanner.v1.KeySet' => function ($v) { + // exit("TEST"); + $keys = $this->pluck('keys', $keySet, false); + if ($keys) { + $keySet['keys'] = array_map( + fn ($key) => $this->formatListForApi((array) $key), + $keys + ); + } + + if (isset($keySet['ranges'])) { + $keySet['ranges'] = array_map(function ($rangeItem) { + return array_map([$this, 'formatListForApi'], $rangeItem); + }, $keySet['ranges']); + + if (empty($keySet['ranges'])) { + unset($keySet['ranges']); + } + } + + return $this->decodeMessage(new KeySet(), $keySet); + }, + ], [], [], [ // A custom encoder that short-circuits the encodeMessage in Serializer class, // but only if the argument is of the type PartialResultSet. PartialResultSet::class => function ($msg) { diff --git a/Spanner/tests/Snippet/ArrayTypeTest.php b/Spanner/tests/Snippet/ArrayTypeTest.php index 53c92a83cf8e..3f36806155b1 100644 --- a/Spanner/tests/Snippet/ArrayTypeTest.php +++ b/Spanner/tests/Snippet/ArrayTypeTest.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner\Tests\Snippet; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; diff --git a/Spanner/tests/Snippet/BackupTest.php b/Spanner/tests/Snippet/BackupTest.php index 0ee5a7d59f65..3dff343dc807 100644 --- a/Spanner/tests/Snippet/BackupTest.php +++ b/Spanner/tests/Snippet/BackupTest.php @@ -23,7 +23,7 @@ use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\DeleteBackupRequest; diff --git a/Spanner/tests/Snippet/Batch/BatchClientTest.php b/Spanner/tests/Snippet/Batch/BatchClientTest.php index f0de9167aa35..fc02fd1dcac1 100644 --- a/Spanner/tests/Snippet/Batch/BatchClientTest.php +++ b/Spanner/tests/Snippet/Batch/BatchClientTest.php @@ -17,7 +17,7 @@ namespace Google\Cloud\Spanner\Tests\Snippet\Batch; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\RequestHandler; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; diff --git a/Spanner/tests/Unit/BackupTest.php b/Spanner/tests/Unit/BackupTest.php index c36f7f3de4b8..71f2cad572af 100644 --- a/Spanner/tests/Unit/BackupTest.php +++ b/Spanner/tests/Unit/BackupTest.php @@ -21,7 +21,7 @@ use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Google\ApiCore\OperationResponse; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Admin\Database\V1\Backup as BackupProto; use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; diff --git a/Spanner/tests/Unit/Batch/BatchClientTest.php b/Spanner/tests/Unit/Batch/BatchClientTest.php index ef1ab09189aa..ae6d56fc11d4 100644 --- a/Spanner/tests/Unit/Batch/BatchClientTest.php +++ b/Spanner/tests/Unit/Batch/BatchClientTest.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Timestamp; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\Batch\BatchClient; diff --git a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php index f610391b8384..11c13acd2507 100644 --- a/Spanner/tests/Unit/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Unit/Batch/BatchSnapshotTest.php @@ -18,7 +18,7 @@ namespace Google\Cloud\Spanner\Tests\Unit\Batch; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Spanner\Batch\BatchSnapshot; use Google\Cloud\Spanner\Batch\PartitionInterface; use Google\Cloud\Spanner\Batch\QueryPartition; @@ -36,6 +36,7 @@ use Google\Cloud\Spanner\V1\PartitionReadRequest; use Google\Cloud\Spanner\V1\PartitionResponse; use Google\Cloud\Spanner\V1\ReadRequest; +use Google\Cloud\Spanner\V1\KeySet as KeySetProto; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; @@ -73,7 +74,28 @@ public function setUp(): void $this->timestamp = new Timestamp(new \DateTime()); - $this->serializer = new Serializer(); + $this->serializer = new Serializer([], [], [], [ + 'google.spanner.v1.KeySet' => function ($keySet) { + $keys = $this->pluck('keys', $keySet, false); + if ($keys) { + $keySet['keys'] = array_map( + fn ($key) => $this->formatListForApi((array) $key), + $keys + ); + } + + if (isset($keySet['ranges'])) { + $keySet['ranges'] = array_map(function ($rangeItem) { + return array_map([$this, 'formatListForApi'], $rangeItem); + }, $keySet['ranges']); + + if (empty($keySet['ranges'])) { + unset($keySet['ranges']); + } + } + return $keySet; + }, + ]); $this->spannerClient = $this->prophesize(SpannerClient::class); $this->snapshot = new BatchSnapshot( @@ -123,6 +145,7 @@ public function testPartitionRead() $this->spannerClient->partitionRead( Argument::that(function (PartitionReadRequest $request) use ($expectedArguments) { $actualArguments = $this->serializer->encodeMessage($request); + // var_dump($actualArguments, $expectedArguments);exit; return $actualArguments == $expectedArguments; }), Argument::type('array') diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index be968c911dc1..0c33ecdaad14 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -28,7 +28,7 @@ use Google\Cloud\Core\Exception\ServiceException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\Snippet\Fixtures; use Google\Cloud\Spanner\Admin\Database\V1\Backup; diff --git a/Spanner/tests/Unit/InstanceConfigurationTest.php b/Spanner/tests/Unit/InstanceConfigurationTest.php index ce9bb8127289..854b143442d0 100644 --- a/Spanner/tests/Unit/InstanceConfigurationTest.php +++ b/Spanner/tests/Unit/InstanceConfigurationTest.php @@ -19,7 +19,7 @@ use Google\ApiCore\ApiException; use Google\ApiCore\OperationResponse; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; diff --git a/Spanner/tests/Unit/InstanceTest.php b/Spanner/tests/Unit/InstanceTest.php index 1f8378fc06d9..9409dcad3d00 100644 --- a/Spanner/tests/Unit/InstanceTest.php +++ b/Spanner/tests/Unit/InstanceTest.php @@ -20,7 +20,7 @@ use Google\ApiCore\OperationResponse; use Google\ApiCore\Page; use Google\ApiCore\PagedListResponse; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Core\Iam\IamManager; use Google\Cloud\Core\Iterator\ItemIterator; diff --git a/Spanner/tests/Unit/OperationTest.php b/Spanner/tests/Unit/OperationTest.php index 12bcc4dc084c..ec7d0d861131 100644 --- a/Spanner/tests/Unit/OperationTest.php +++ b/Spanner/tests/Unit/OperationTest.php @@ -19,7 +19,7 @@ use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; diff --git a/Spanner/tests/Unit/SpannerClientTest.php b/Spanner/tests/Unit/SpannerClientTest.php index 03eed6411aac..1abfdfafd0b8 100644 --- a/Spanner/tests/Unit/SpannerClientTest.php +++ b/Spanner/tests/Unit/SpannerClientTest.php @@ -20,7 +20,7 @@ use Google\ApiCore\OperationResponse; use Google\ApiCore\Page; use Google\ApiCore\PagedListResponse; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Int64; use Google\Cloud\Core\Iterator\ItemIterator; use Google\Cloud\Core\Testing\GrpcTestTrait; diff --git a/Spanner/tests/Unit/TransactionTest.php b/Spanner/tests/Unit/TransactionTest.php index 2eeaf0e6d45e..fbbf361386dc 100644 --- a/Spanner/tests/Unit/TransactionTest.php +++ b/Spanner/tests/Unit/TransactionTest.php @@ -19,7 +19,7 @@ use Google\ApiCore\ValidationException; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Spanner\BatchDmlResult; diff --git a/Spanner/tests/Unit/TransactionTypeTest.php b/Spanner/tests/Unit/TransactionTypeTest.php index 65ee16096306..5b672f81017f 100644 --- a/Spanner/tests/Unit/TransactionTypeTest.php +++ b/Spanner/tests/Unit/TransactionTypeTest.php @@ -17,7 +17,7 @@ namespace Google\Cloud\Spanner\Tests\Unit; -use Google\Cloud\Core\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Testing\GrpcTestTrait; From 390ba8be1c432cb886ce2b243a318e3c6227e76d Mon Sep 17 00:00:00 2001 From: Brent Shaffer <betterbrent@google.com> Date: Fri, 15 Nov 2024 16:45:22 -0800 Subject: [PATCH 8/8] LRO cleanup --- Spanner/src/Backup.php | 41 ++---- Spanner/src/Database.php | 139 +++++++++--------- Spanner/src/Instance.php | 53 ++++++- Spanner/src/InstanceConfiguration.php | 29 +++- Spanner/src/Operation.php | 14 +- Spanner/src/RequestTrait.php | 55 +++++++ Spanner/src/Session/Session.php | 4 +- Spanner/src/SpannerClient.php | 53 +++---- Spanner/src/Transaction.php | 4 +- Spanner/src/TransactionalReadTrait.php | 13 +- .../tests/Snippet/Batch/BatchSnapshotTest.php | 1 + Spanner/tests/Unit/InstanceTest.php | 2 + 12 files changed, 250 insertions(+), 158 deletions(-) diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index fb37ef528cfd..f050e4f42f08 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -35,7 +35,6 @@ use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest; use Google\Cloud\Spanner\Admin\Database\V1\UpdateBackupRequest; use Google\LongRunning\ListOperationsRequest; -use Google\LongRunning\Operation; /** * Represents a Cloud Spanner Backup. @@ -104,7 +103,7 @@ public function __construct( */ public function create($database, DateTimeInterface $expireTime, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data = $this->validateAndFormatVersionTime($data); $data += [ @@ -153,7 +152,7 @@ public function create($database, DateTimeInterface $expireTime, array $options */ public function createCopy(Backup $newBackup, DateTimeInterface $expireTime, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'parent' => $newBackup->instance->name(), 'backupId' => DatabaseAdminClient::parseName($newBackup->name)['backup'], @@ -181,7 +180,7 @@ public function createCopy(Backup $newBackup, DateTimeInterface $expireTime, arr */ public function delete(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'name' => $this->name ]; @@ -265,7 +264,7 @@ public function name() */ public function reload(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'name' => $this->name ]; @@ -322,7 +321,7 @@ public function state(array $options = []) */ public function updateExpireTime(DateTimeInterface $newTimestamp, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'backup' => [ 'name' => $this->name(), @@ -386,34 +385,14 @@ public function resumeOperation($operationName, array $options = []) */ public function longRunningOperations(array $options = []) { - $options += ['name' => $this->name . '/operations']; - $resultLimit = $this->pluck('resultLimit', $options, false) ?: 0; [$data, $callOptions] = $this->splitOptionalArgs($options); $request = $this->serializer->decodeMessage(new ListOperationsRequest(), $data); + $request->setName($this->name . '/operations'); - return new ItemIterator( - new PageIterator( - function (Operation $operation) { - return $this->resumeOperation( - $operation->getName(), - ['lastProtoResponse' => $operation] - ); - }, - function (array $args) { - $nextPageToken = $this->pluck('pageToken', $args, false) ?: null; - $operationsClient = $this->databaseAdminClient->getOperationsClient(); - $page = $operationsClient->listOperations(...$args)->getPage(); - return [ - 'operations' => iterator_to_array($page->getResponseObject()->getOperations()), - 'nextResultTokenPath' => $page->getNextPageToken(), - ]; - }, - [$request, $callOptions], - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) + return $this->buildLongRunningIterator( + [$this->databaseAdminClient->getOperationsClient(), 'listOperations'], + $request, + $callOptions ); } diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index b2be249343d6..6e69046e5bd2 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -55,6 +55,7 @@ use Google\Cloud\Spanner\V1\Mutation\Delete; use Google\Cloud\Spanner\V1\Mutation\Write; use Google\Cloud\Spanner\V1\TypeCode; +use Google\LongRunning\ListOperationsRequest; use Google\Protobuf\Duration; use Google\Protobuf\ListValue; use Google\Protobuf\Struct; @@ -353,7 +354,7 @@ public function info(array $options = []): array */ public function reload(array $options = []): array { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['name'] = $this->name; $request = $this->serializer->decodeMessage(new GetDatabaseRequest(), $data); @@ -412,7 +413,7 @@ public function exists(array $options = []): bool */ public function create(array $options = []): OperationResponse { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $dialect = $data['databaseDialect'] ?? DatabaseDialect::DATABASE_DIALECT_UNSPECIFIED; $data += [ @@ -471,7 +472,7 @@ public function restore($backup, array $options = []): OperationResponse */ public function updateDatabase(array $options = []): OperationResponse { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $fieldMask = []; if (isset($data['enableDropProtection'])) { @@ -555,7 +556,7 @@ public function updateDdl($statement, array $options = []): OperationResponse */ public function updateDdlBatch(array $statements, array $options = []): OperationResponse { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'database' => $this->name, 'statements' => $statements @@ -591,7 +592,7 @@ public function updateDdlBatch(array $statements, array $options = []): Operatio */ public function drop(array $options = []): void { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new DropDatabaseRequest(), $data); @@ -628,7 +629,7 @@ public function drop(array $options = []): void */ public function ddl(array $options = []): array { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new GetDatabaseDdlRequest(), $data); @@ -1774,7 +1775,7 @@ public function batchWrite(array $mutationGroups, array $options = []): \Generat ); try { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'session' => $session->name(), 'mutationGroups' => $mutationGroups @@ -2198,7 +2199,7 @@ public function identity(): array */ public function batchCreateSessions(array $options): array { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new BatchCreateSessionsRequest(), $data); @@ -2221,7 +2222,7 @@ public function batchCreateSessions(array $options): array */ public function deleteSessionAsync(array $options): PromiseInterface { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); @@ -2252,35 +2253,15 @@ public function deleteSessionAsync(array $options): PromiseInterface */ public function backupOperations(array $options = []): ItemIterator { - list($data, $callOptions) = $this->splitOptionalArgs($options); - $data['parent'] = $this->instance->name(); - - $resultLimit = $this->pluck('resultLimit', $options, false); - return new ItemIterator( - new PageIterator( - function (array $operation) { - return new OperationResponse( - $operation['name'], - $this->databaseAdminClient->getOperationsClient(), - ); - }, - function ($callOptions) use ($data) { - if (isset($callOptions['pageToken'])) { - $data['pageToken'] = $callOptions['pageToken']; - } + [$data, $callOptions] = $this->splitOptionalArgs($options); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + $request = $this->serializer->decodeMessage(new ListBackupOperationsRequest(), $data); + $request->setParent($this->instance->name()); - $request = $this->serializer->decodeMessage(new ListBackupOperationsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - - $response = $this->databaseAdminClient->listBackupOperations($request, $callOptions); - return $this->handleResponse($response); - }, - $callOptions, - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) + return $this->buildLongRunningIterator( + [$this->databaseAdminClient, 'listBackupOperations'], + $request, + $callOptions ); } @@ -2297,7 +2278,7 @@ function ($callOptions) use ($data) { */ public function createDatabaseFromBackup($name, $backup, array $options = []): OperationResponse { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'parent' => $this->instance->name(), 'databaseId' => $this->databaseIdOnly($name), @@ -2334,35 +2315,15 @@ public function createDatabaseFromBackup($name, $backup, array $options = []): O */ public function databaseOperations(array $options = []): ItemIterator { - list($data, $callOptions) = $this->splitOptionalArgs($options); - $data['parent'] = $this->instance->name(); - - $resultLimit = $this->pluck('resultLimit', $options, false); - return new ItemIterator( - new PageIterator( - function (array $operation) { - return new OperationResponse( - $operation['name'], - $this->databaseAdminClient->getOperationsClient(), - ); - }, - function ($callOptions) use ($data) { - if (isset($callOptions['pageToken'])) { - $data['pageToken'] = $callOptions['pageToken']; - } + [$data, $callOptions] = $this->splitOptionalArgs($options); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); + $request = $this->serializer->decodeMessage(new ListDatabaseOperationsRequest(), $data); + $request->setParent($this->instance->name()); - $request = $this->serializer->decodeMessage(new ListDatabaseOperationsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - - $response = $this->databaseAdminClient->listDatabaseOperations($request, $callOptions); - return $this->handleResponse($response); - }, - $callOptions, - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) + return $this->buildLongRunningIterator( + [$this->databaseAdminClient, 'listDatabaseOperations'], + $request, + $callOptions ); } @@ -2377,11 +2338,47 @@ function ($callOptions) use ($data) { * @param string $operationName The Long Running Operation name. * @return OperationResponse */ - public function resumeOperation($operationName): OperationResponse + public function resumeOperation($operationName, array $options = []) { - return new OperationResponse( + return (new OperationResponse( $operationName, - $this->databaseAdminClient->getOperationsClient() + $this->databaseAdminClient->getOperationsClient(), + $options + ))->withResultFunction($this->databaseResultFunction()); + } + + /** + * List long running operations. + * + * Example: + * ``` + * $operations = $backup->longRunningOperations(); + * ``` + * + * @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 PagedListResponse<OperationResponse> + */ + public function longRunningOperations(array $options = []) + { + [$data, $callOptions] = $this->splitOptionalArgs($options); + $request = $this->serializer->decodeMessage(new ListOperationsRequest(), $data); + $request->setName($this->name . '/operations'); + + return $this->buildLongRunningIterator( + [$this->databaseAdminClient->getOperationsClient(), 'listOperations'], + $request, + $callOptions ); } @@ -2507,10 +2504,6 @@ private function parseMutations(array $rawMutations): array switch ($type) { case Operation::OP_DELETE: - if (isset($data['keySet'])) { - $data['keySet'] = $this->formatKeySet($data['keySet']); - } - $operation = $this->serializer->decodeMessage( new Delete(), $data diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 8dce44413d35..1b2617915fba 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -39,6 +39,7 @@ use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceRequest; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\LongRunning\ListOperationsRequest; /** * Represents a Cloud Spanner instance @@ -193,7 +194,7 @@ public function info(array $options = []) */ public function exists(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); try { if ($this->info) { $data += [ @@ -237,7 +238,7 @@ public function exists(array $options = []) */ public function reload(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'name' => $this->name ]; @@ -408,7 +409,7 @@ public function update(array $options = []) */ public function delete(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['name'] = $this->name; $request = $this->serializer->decodeMessage(new DeleteInstanceRequest(), $data); @@ -538,7 +539,7 @@ public function database($name, array $options = []) */ public function databases(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['parent'] = $this->name; $resultLimit = $this->pluck('resultLimit', $data, false); @@ -624,7 +625,7 @@ public function backup($name, array $backup = []) */ public function backups(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['parent'] = $this->name; $resultLimit = $this->pluck('resultLimit', $options, false); @@ -829,11 +830,47 @@ public function createInstanceArray(array $instanceArray, InstanceConfiguration * @param string $operationName The Long Running Operation name. * @return OperationResponse */ - public function resumeOperation($operationName) + public function resumeOperation($operationName, array $options = []) { - return new OperationResponse( + return (new OperationResponse( $operationName, - $this->instanceAdminClient->getOperationsClient() + $this->instanceAdminClient->getOperationsClient(), + $options + ))->withResultFunction($this->instanceResultFunction()); + } + + /** + * List long running operations. + * + * Example: + * ``` + * $operations = $backup->longRunningOperations(); + * ``` + * + * @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 PagedListResponse<OperationResponse> + */ + public function longRunningOperations(array $options = []) + { + [$data, $callOptions] = $this->splitOptionalArgs($options); + $request = $this->serializer->decodeMessage(new ListOperationsRequest(), $data); + $request->setName($this->name . '/operations'); + + return $this->buildLongRunningIterator( + [$this->instanceAdminClient->getOperationsClient(), 'listOperations'], + $request, + $callOptions ); } diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index fc54c16156ae..4420e1ea1756 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -31,6 +31,7 @@ use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig\Type; use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; use Google\Cloud\Spanner\Admin\Instance\V1\UpdateInstanceConfigRequest; +use Google\LongRunning\ListOperationsRequest; use Google\Rpc\Code; /** @@ -170,7 +171,7 @@ public function exists(array $options = []) */ public function reload(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += ['name' => $this->name]; $callOptions = $this->addResourcePrefixHeader( $callOptions, @@ -221,7 +222,7 @@ public function reload(array $options = []) */ public function create(InstanceConfiguration $baseConfig, array $replicas, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $leaderOptions = $baseConfig->__debugInfo()['info']['leaderOptions'] ?? []; $validateOnly = $data['validateOnly'] ?? false; @@ -283,7 +284,7 @@ public function create(InstanceConfiguration $baseConfig, array $replicas, array */ public function update(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $validateOnly = $data['validateOnly'] ?? false; unset($data['validateOnly']); $data += ['name' => $this->name]; @@ -322,7 +323,7 @@ public function update(array $options = []) */ public function delete(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += ['name' => $this->name]; $this->instanceAdminClient->deleteInstanceConfig( @@ -331,6 +332,26 @@ public function delete(array $options = []) ); } + /** + * Resume a Long Running Operation + * + * Example: + * ``` + * $operation = $spanner->resumeOperation($operationName); + * ``` + * + * @param string $operationName The Long Running Operation name. + * @return OperationResponse + */ + public function resumeOperation($operationName, array $options = []) + { + return (new OperationResponse( + $operationName, + $this->instanceAdminClient->getOperationsClient(), + $options + ))->withResultFunction($this->instanceConfigResultFunction()); + } + /** * Get the fully qualified instance config name. * diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index 64e218234b38..96a215ab4fe8 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -153,7 +153,7 @@ public function commit(Session $session, array $mutations, array $options = []) */ public function commitWithResponse(Session $session, array $mutations, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $mutations = $this->serializeMutations($mutations); $data += [ 'transactionId' => null, @@ -195,7 +195,7 @@ public function rollback(Session $session, $transactionId, array $options = []) throw new InvalidArgumentException('Rollback failed: Transaction not initiated.'); } - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data = [ 'session' => $session->name(), 'transactionId' => $transactionId @@ -369,7 +369,7 @@ public function executeUpdateBatch( array $statements, array $options = [] ) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['transaction'] = $this->createTransactionSelector($data, $transaction->id()); $data += [ 'session' => $session->name(), @@ -647,7 +647,7 @@ public function createSnapshot(Session $session, array $res = [], $className = S */ public function createSession($databaseName, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data = [ 'database' => $databaseName, 'session' => [ @@ -732,7 +732,7 @@ public function partitionQuery(Session $session, $transactionId, $sql, array $op { // cache this to pass to the partition instance. $originalOptions = $options; - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data = $this->formatPartitionQueryOptions($data); $data += [ @@ -795,7 +795,7 @@ public function partitionRead( ) { // cache this to pass to the partition instance. $originalOptions = $options; - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'transaction' => $this->createTransactionSelector($data, $transactionId), 'session' => $session->name(), @@ -853,7 +853,7 @@ private function partitionOptions(array &$options) */ private function beginTransaction(Session $session, array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $transactionOptions = $this->formatTransactionOptions( $this->pluck('transactionOptions', $data, false) ?: [] ); diff --git a/Spanner/src/RequestTrait.php b/Spanner/src/RequestTrait.php index 663fad17c158..9c5e61d82e2a 100644 --- a/Spanner/src/RequestTrait.php +++ b/Spanner/src/RequestTrait.php @@ -17,9 +17,14 @@ namespace Google\Cloud\Spanner; +use Google\ApiCore\ApiException; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\Iterator\ItemIterator; +use Google\Cloud\Core\Iterator\PageIterator; use Google\Cloud\Core\RequestProcessorTrait; +use Google\LongRunning\Operation; +use Google\Protobuf\Internal\Message; /** * Shared functionality for Spanner requests. @@ -69,4 +74,54 @@ private function addResourcePrefixHeader(array $args, string $value) $args['headers'][$this->resourcePrefixHeader] = [$value]; return $args; } + + /** + * Helper making list calls for long running operations. + * + * + * @param callable $call The GAPIC client and method for the list operations request + * @param Message $request The list operations request + * @param array $callOptions [optional] Call options for the request + * @param callable $operationResponseMapper [optional] A callable to map the Operation to an + * operation response. Defaults to `$this->resumeOperation()`. + * @return ItemIterator<OperationResponse> + */ + private function buildLongRunningIterator( + callable $call, + Message $request, + array $callOptions = [], + ?callable $operationResponseMapper = null + ): ItemIterator { + $resultLimit = $this->pluck('resultLimit', $callOptions, false) ?: 0; + return new ItemIterator( + new PageIterator( + $operationResponseMapper ?: function (Operation $operation) { + return $this->resumeOperation( + $operation->getName(), + ['lastProtoResponse' => $operation] + ); + }, + function (array $args) use ($call) { + if ($pageToken = $this->pluck('pageToken', $args, false) ?: null) { + $args['request']->setPageToken($pageToken); + } + try { + $page = $call($args['request'], $args['callOptions'])->getPage(); + } catch (ApiException $e) { + throw $this->convertToGoogleException($e); + } + return [ + 'operations' => iterator_to_array($page->getResponseObject()->getOperations()), + 'nextResultToken' => $page->getNextPageToken(), + ]; + }, [ + 'request' => $request, + 'callOptions' => $callOptions + ], [ + 'itemsKey' => 'operations', + 'resultLimit' => $resultLimit + ] + ) + ); + } } diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index fefed790e390..1985059f644e 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -112,7 +112,7 @@ public function info() */ public function exists(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += [ 'name' => $this->name() ]; @@ -137,7 +137,7 @@ public function exists(array $options = []) */ public function delete(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data = [ 'name' => $this->name() ]; diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 8883c8a4ec88..e5fa9d6dddae 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -42,6 +42,7 @@ use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Session\SessionPoolInterface; use Google\Cloud\Spanner\V1\Client\SpannerClient as GapicSpannerClient; +use Google\LongRunning\ListOperationsRequest; use Google\Protobuf\Duration; use Psr\Cache\CacheItemPoolInterface; use Psr\Http\StreamInterface; @@ -465,7 +466,7 @@ public function createInstanceConfiguration(InstanceConfiguration $baseConfig, $ */ public function instanceConfigurations(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data['parent'] = $this->projectName; $resultLimit = $this->pluck('resultLimit', $options, false) ?: 0; @@ -555,31 +556,24 @@ public function instanceConfiguration($name, array $options = []) */ public function instanceConfigOperations(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); - $resultLimit = $this->pluck('resultLimit', $options, false); - $data['parent'] = $this->projectName; - return new ItemIterator( - new PageIterator( - function (OperationResponse $operation) { - return $operation; - }, - function ($callOptions) use ($data) { - if (isset($callOptions['pageToken'])) { - $data['pageToken'] = $callOptions['pageToken']; - } - - $request = $this->serializer->decodeMessage(new ListInstanceConfigOperationsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); - - $response = $this->instanceAdminClient->listInstanceConfigOperations($request, $callOptions); - return $this->handleResponse($response); - }, - $callOptions, - [ - 'itemsKey' => 'operations', - 'resultLimit' => $resultLimit - ] - ) + [$data, $callOptions] = $this->splitOptionalArgs($options); + $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); + $request = $this->serializer->decodeMessage(new ListInstanceConfigOperationsRequest(), $data); + $request->setParent($this->projectName); + + return $this->buildLongRunningIterator( + [$this->instanceAdminClient, 'listInstanceConfigOperations'], + $request, + $callOptions, + function (Operation $operation) { + return $this->resumeOperation( + $operation->getName(), + ['lastProtoResponse' => $operation] + )->withResultFunction(function (InstanceConfig $result) { + $config = $this->serializer->encodeMessage($result); + return $this->instanceConfiguration($config['name'], $config); + }); + }, ); } @@ -671,7 +665,7 @@ public function instance($name, array $instance = []) */ public function instances(array $options = []) { - list($data, $callOptions) = $this->splitOptionalArgs($options); + [$data, $callOptions] = $this->splitOptionalArgs($options); $data += ['filter' => '', 'parent' => $this->projectName]; $resultLimit = $this->pluck('resultLimit', $data, false); @@ -750,11 +744,12 @@ public function connect($instance, $name, array $options = []) * @param string $operationName The Long Running Operation name. * @return OperationResponse */ - public function resumeOperation($operationName) + public function resumeOperation($operationName, array $options = []) { return new OperationResponse( $operationName, - $this->databaseAdminClient->getOperationsClient() + $this->databaseAdminClient->getOperationsClient(), + $options ); } diff --git a/Spanner/src/Transaction.php b/Spanner/src/Transaction.php index 2255aab9f5bd..ac62ad8deeaf 100644 --- a/Spanner/src/Transaction.php +++ b/Spanner/src/Transaction.php @@ -517,6 +517,8 @@ private function buildUpdateOptions(array $options): array $selector = $this->transactionSelector($options); $options['transaction'] = $selector[0]; - return $this->addLarHeader($options); + $options['headers']['spanner-route-to-leader'] = ['true']; + + return $options; } } diff --git a/Spanner/src/TransactionalReadTrait.php b/Spanner/src/TransactionalReadTrait.php index 5a64826fa3bd..b80415e3ce82 100644 --- a/Spanner/src/TransactionalReadTrait.php +++ b/Spanner/src/TransactionalReadTrait.php @@ -26,7 +26,7 @@ trait TransactionalReadTrait { use TransactionConfigurationTrait; - use RequestTrait; + // use RequestTrait; /** * @var Operation @@ -303,7 +303,11 @@ public function execute($sql, array $options = []) $this->directedReadOptions ?? [] ); - $options = $this->addLarHeader($options, true, $this->context); + if ($this->context === SessionPoolInterface::CONTEXT_READWRITE) { + // add LAR header + $options['headers']['x-goog-spanner-route-to-leader'] = ['true']; + } + // Unsetting the internal flag unset($options['singleUse']); @@ -385,7 +389,10 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] $this->directedReadOptions ?? [] ); - $options = $this->addLarHeader($options, true, $this->context); + if ($this->context === SessionPoolInterface::CONTEXT_READWRITE) { + // add LAR header + $options['headers']['x-goog-spanner-route-to-leader'] = ['true']; + } $result = $this->operation->read($this->session, $table, $keySet, $columns, $options); if (empty($this->id()) && $result->transaction()) { diff --git a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php index bbce2e7c91d6..2ef49adafb54 100644 --- a/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php +++ b/Spanner/tests/Snippet/Batch/BatchSnapshotTest.php @@ -27,6 +27,7 @@ use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Operation; use Google\Cloud\Spanner\Result; +use Google\Cloud\Spanner\Serializer; use Google\Cloud\Spanner\Session\Session; use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\V1\Client\SpannerClient; diff --git a/Spanner/tests/Unit/InstanceTest.php b/Spanner/tests/Unit/InstanceTest.php index 9409dcad3d00..41ee2ed4d730 100644 --- a/Spanner/tests/Unit/InstanceTest.php +++ b/Spanner/tests/Unit/InstanceTest.php @@ -604,6 +604,7 @@ public function testBackupOperations() ]; $this->page->getResponseObject()->willReturn(new ListBackupOperationsResponse(['operations' => $operations])); + $this->page->getNextPageToken()->willReturn(null); $this->databaseAdminClient->listBackupOperations( Argument::that(function ($request) { @@ -637,6 +638,7 @@ public function testListDatabaseOperations() ]; $this->page->getResponseObject()->willReturn(new ListDatabaseOperationsResponse(['operations' => $operations])); + $this->page->getNextPageToken()->willReturn(null); $this->databaseAdminClient->listDatabaseOperations( Argument::that(function ($request) {