Skip to content

Commit bfc0dd6

Browse files
authored
feat!: Spanner V2 (#7841)
1 parent 670112d commit bfc0dd6

File tree

184 files changed

+10446
-25604
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

184 files changed

+10446
-25604
lines changed

.github/run-package-tests.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@
1515

1616
set -e
1717

18+
# USAGE:
19+
#
20+
# run-package-tests.sh [DIRECTORY] [PREFER_LOWEST]
21+
#
22+
# DIRECTORY: Optionally pass in a component directory and only run the script
23+
#. for that component.
24+
#
25+
# PREFER_LOWEST: can be "--prefer-lowest" or "--prefer-lowest-strict". When the
26+
# "--prefer-lowest-strict" flag is set, local package dependencies
27+
# are installed according to their version number in
28+
# `[Component]/VERSION`. This flag is set on the release PRs to
29+
#. ensure the dependencies for the upcoming release will be
30+
# configured correctly.
31+
1832
DIRS=$(find * -maxdepth 0 -type d -name '[A-Z]*')
1933
PREFER_LOWEST=""
2034
if [ "$#" -eq 1 ]; then

.github/workflows/emulator-system-tests-spanner.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
- name: Install dependencies
5050
run: |
5151
# ensure composer uses local Core instead of pulling from packagist
52-
composer config repositories.local --json '{"type":"path", "url": "../Core", "options": {"versions": {"google/cloud-core": "1.100"}}}' -d Spanner
52+
composer config repositories.local --json '{"type":"path", "url": "../Core", "options": {"versions": {"google/cloud-core": "1.100"}},"canonical":false}' -d Spanner
5353
composer update --prefer-dist --no-interaction --no-suggest -d Spanner/
5454
5555
- name: Run system tests

Core/src/ApiHelperTrait.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Google\ApiCore\Options\CallOptions;
2222
use Google\Protobuf\Internal\Message;
2323
use Google\Protobuf\NullValue;
24+
use LogicException;
2425

2526
/**
2627
* @internal
@@ -276,6 +277,8 @@ private function splitOptionalArgs(array $input, array $extraAllowedKeys = []):
276277
* $optionTypes can be an array of string keys, a protobuf Message classname, or a
277278
* the CallOptions classname. Parameters are split and returned in the order
278279
* that the options types are provided.
280+
*
281+
* @throws LogicException
279282
*/
280283
private function validateOptions(array $options, array|Message|string ...$optionTypes): array
281284
{

Core/src/Iam/Iam.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*
3535
* use Google\Cloud\Spanner\SpannerClient;
3636
*
37-
* $spanner = new SpannerClient();
37+
* $spanner = new SpannerClient(['projectId' => 'my-project']);
3838
* $instance = $spanner->instance('my-new-instance');
3939
*
4040
* $iam = $instance->iam();
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Cloud\Core\LongRunning;
19+
20+
use Google\ApiCore\OperationResponse;
21+
use Google\ApiCore\Serializer;
22+
use Google\Cloud\Core\RequestProcessorTrait;
23+
use Google\LongRunning\ListOperationsRequest;
24+
use Google\Protobuf\Any;
25+
26+
/**
27+
* Defines the calls required to manage Long Running Operations using a GAPIC
28+
* generated client.
29+
*
30+
* @internal
31+
*/
32+
class LongRunningClientConnection implements LongRunningConnectionInterface
33+
{
34+
use RequestProcessorTrait;
35+
36+
public function __construct(
37+
private object $gapicClient,
38+
private Serializer $serializer
39+
) {
40+
}
41+
42+
/**
43+
* @param array $args
44+
* @return array
45+
*/
46+
public function get(array $args): array
47+
{
48+
$operationResponse = $this->gapicClient->resumeOperation($args['name']);
49+
50+
return $this->operationResponseToArray($operationResponse);
51+
}
52+
53+
/**
54+
* @param array $args
55+
* @return array
56+
*/
57+
public function cancel(array $args): array
58+
{
59+
$operationResponse = $this->gapicClient->resumeOperation(
60+
$args['name'],
61+
$args['method'] ?? null
62+
);
63+
$operationResponse->cancel();
64+
65+
return $this->operationResponseToArray($operationResponse);
66+
}
67+
68+
/**
69+
* @param array $args
70+
* @return array
71+
*/
72+
public function delete(array $args): array
73+
{
74+
$operationResponse = $this->gapicClient->resumeOperation(
75+
$args['name'],
76+
$args['method'] ?? null
77+
);
78+
$operationResponse->cancel();
79+
80+
return $this->operationResponseToArray($operationResponse);
81+
}
82+
83+
/**
84+
* @param array $args
85+
* @return array
86+
*/
87+
public function operations(array $args): array
88+
{
89+
$request = ListOperationsRequest::build($args['name'], $args['filter'] ?? null);
90+
$response = $this->gapicClient->getOperationsClient()->listOperations($request);
91+
92+
return $this->handleResponse($response);
93+
}
94+
95+
private function operationResponseToArray(OperationResponse $operationResponse): array
96+
{
97+
$response = $this->handleResponse($operationResponse->getLastProtoResponse());
98+
$metaType = $response['metadata']['typeUrl'];
99+
100+
// unpack result Any type
101+
$result = $operationResponse->getResult();
102+
if ($result instanceof Any) {
103+
// For some reason we aren't doing this in GAX OperationResponse (but we should)
104+
$result = $result->unpack();
105+
}
106+
$response['response'] = $this->handleResponse($result);
107+
108+
// unpack error Any type
109+
$response['error'] = $this->handleResponse($operationResponse->getError());
110+
111+
$metadata = $operationResponse->getMetadata();
112+
if ($metadata instanceof Any) {
113+
// For some reason we aren't doing this in GAX OperationResponse (but we should)
114+
$metadata = $metadata->unpack();
115+
}
116+
$response['metadata'] = $this->handleResponse($metadata);
117+
118+
// Used in LongRunningOperation to invoke callables
119+
$response['metadata'] += ['typeUrl' => $metaType];
120+
121+
return $response;
122+
}
123+
}

Core/src/LongRunning/LongRunningOperation.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
/**
2121
* Represent and interact with a Long Running Operation.
22+
* @template T
2223
*/
2324
class LongRunningOperation
2425
{
@@ -180,7 +181,7 @@ public function state(array $options = [])
180181
* ```
181182
*
182183
* @param array $options [optional] Configuration options.
183-
* @return mixed|null
184+
* @return T|mixed|null
184185
*/
185186
public function result(array $options = [])
186187
{
@@ -252,12 +253,11 @@ public function reload(array $options = [])
252253

253254
$this->result = null;
254255
$this->error = null;
255-
if (isset($res['done']) && $res['done']) {
256+
257+
if ($res['done'] ?? false && isset($res['metadata']['typeUrl'])) {
256258
$type = $res['metadata']['typeUrl'];
257259
$this->result = $this->executeDoneCallback($type, $res['response']);
258-
$this->error = (isset($res['error']))
259-
? $res['error']
260-
: null;
260+
$this->error = $res['error'] ?? null;
261261
}
262262

263263
return $this->info = $res;

Core/src/OptionsValidator.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ class OptionsValidator
3636
* @param ?Serializer $serializer use a serializer to decode protobuf messages
3737
* instead of calling {@see Message::mergeFromJsonString()}.
3838
*/
39-
public function __construct(private ?Serializer $serializer = null)
40-
{
39+
public function __construct(
40+
private ?Serializer $serializer = null
41+
) {
4142
}
4243

4344
/**
@@ -90,6 +91,8 @@ public function validateOptions(array $options, array|Message|string ...$optionT
9091
$optionType->mergeFromJsonString(json_encode($messageOptions, JSON_FORCE_OBJECT));
9192
}
9293
$splitOptions[] = $optionType;
94+
} elseif (is_string($optionType)) {
95+
$splitOptions[] = $this->pluck($optionType, $options, false);
9396
} else {
9497
throw new LogicException(sprintf('Invalid option type: %s', $optionType));
9598
}

Core/src/RequestHandler.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ class RequestHandler
4242
*/
4343
private Serializer $serializer;
4444

45-
private array $clients;
45+
private array $clients = [];
4646

4747
/**
4848
* @param Serializer $serializer
49-
* @param array $clientClasses
49+
* @param array<string|object> $clients
5050
* @param array $clientConfig
5151
*/
5252
public function __construct(
5353
Serializer $serializer,
54-
array $clientClasses,
54+
array $clients,
5555
array $clientConfig = []
5656
) {
5757
//@codeCoverageIgnoreStart
@@ -75,9 +75,12 @@ public function __construct(
7575
//@codeCoverageIgnoreEnd
7676

7777
// Initialize the client classes and store them in memory
78-
$this->clients = [];
79-
foreach ($clientClasses as $className) {
80-
$this->clients[$className] = new $className($clientConfig);
78+
foreach ($clients as $client) {
79+
if (is_object($client)) {
80+
$this->clients[get_class($client)] = $client;
81+
} else {
82+
$this->clients[$client] = new $client($clientConfig);
83+
}
8184
}
8285
}
8386

Core/src/ServiceBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public function pubsub(array $config = [])
257257
*
258258
* Example:
259259
* ```
260-
* $spanner = $cloud->spanner();
260+
* $spanner = $cloud->spanner(['projectId' => 'my-project']);
261261
* ```
262262
*
263263
* @param array $config [optional] {

Core/src/TimeTrait.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ private function formatTimeAsString(\DateTimeInterface $dateTime, $ns)
7979
$dateTime = $dateTime->setTimeZone(new \DateTimeZone('UTC'));
8080
if ($ns === null) {
8181
return $dateTime->format(Timestamp::FORMAT);
82-
} else {
83-
return sprintf(
84-
$dateTime->format(Timestamp::FORMAT_INTERPOLATE),
85-
$this->convertNanoSecondsToFraction($ns)
86-
);
8782
}
83+
84+
return sprintf(
85+
$dateTime->format(Timestamp::FORMAT_INTERPOLATE),
86+
$this->convertNanoSecondsToFraction($ns)
87+
);
8888
}
8989

9090
/**
@@ -95,10 +95,10 @@ private function formatTimeAsString(\DateTimeInterface $dateTime, $ns)
9595
* $dateTime will be used instead.
9696
* @return array
9797
*/
98-
private function formatTimeAsArray(\DateTimeInterface $dateTime, $ns)
98+
private function formatTimeAsArray(\DateTimeInterface $dateTime, $ns = null)
9999
{
100100
if ($ns === null) {
101-
$ns = $dateTime->format('u');
101+
$ns = $this->convertFractionToNanoSeconds($dateTime->format('u'));
102102
}
103103
return [
104104
'seconds' => (int) $dateTime->format('U'),

0 commit comments

Comments
 (0)