Skip to content

Commit

Permalink
Introduce support for Cloud Spanner (googleapis#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdpedrie authored and dwsupplee committed Apr 7, 2017
1 parent 24a5fab commit ee7aa4a
Show file tree
Hide file tree
Showing 111 changed files with 15,648 additions and 255 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
"psr-4": {
"Google\\Cloud\\Dev\\": "dev/src",
"Google\\Cloud\\Tests\\System\\": "tests/system"
}
},
"files": ["dev/src/Functions.php"]
},
"scripts": {
"google-cloud": "dev/google-cloud"
Expand Down
29 changes: 29 additions & 0 deletions dev/src/Functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Google\Cloud\Dev;

/**
* Create a test stub which extends a real class and allows overriding of private properties.
*
* @param string $extends The fully-qualified name of the class to extend.
* @param array $args An array of constructor arguments to use when creating the stub.
* @param array $props A list of private properties on which to enable overrriding.
* @return mixed
*/
function stub($extends, array $args = [], array $props = [])
{
if (empty($props)) {
$props = ['connection'];
}

$tpl = 'class %s extends %s {private $___props = \'%s\'; use \Google\Cloud\Dev\StubTrait; }';

$name = 'Stub'. sha1($extends);

if (!class_exists($name)) {
eval(sprintf($tpl, $name, $extends, json_encode($props)));
}

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

namespace Google\Cloud\Dev;

trait StubTrait
{
public function ___getProperty($prop)
{
$property = $this->___getPropertyReflector($prop);

$property->setAccessible(true);
return $property->getValue($this);
}

public function ___setProperty($prop, $value)
{
if (!in_array($prop, json_decode($this->___props))) {
throw new \BadMethodCallException(sprintf('Property %s cannot be overloaded', $prop));
}

$property = $this->___getPropertyReflector($prop);

$property->setAccessible(true);
$property->setValue($this, $value);
}

private function ___getPropertyReflector($property)
{
$trait = new \ReflectionClass($this);
$ref = $trait->getParentClass();

try {
$property = $ref->getProperty($property);
} catch (\ReflectionException $e) {
throw new \BadMethodCallException($e->getMessage());
}

return $property;
}
}
2 changes: 2 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<directory suffix=".php">src</directory>
<exclude>
<directory suffix=".php">src/*/V[!a-zA-Z]*</directory>
<directory suffix=".php">src/*/*/V[!a-zA-Z]*</directory>
<directory suffix=".php">src/*/*/*/V[!a-zA-Z]*</directory>
</exclude>
</whitelist>
</filter>
Expand Down
17 changes: 17 additions & 0 deletions src/Core/ArrayTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,21 @@ private function isAssoc(array $arr)
{
return array_keys($arr) !== range(0, count($arr) - 1);
}

/**
* Just like array_filter(), but preserves falsey values except null.
*
* @param array $arr
* @return array
*/
private function arrayFilterRemoveNull(array $arr)
{
return array_filter($arr, function ($element) {
if (!is_null($element)) {
return true;
}

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

namespace Google\Cloud\Core\Exception;

/**
* Exception thrown when a transaction is aborted.
*/
class AbortedException extends ServiceException
{
public function getRetryDelay()
{
$metadata = array_filter($this->options, function ($metadataItem) {
if (array_key_exists('retryDelay', $metadataItem)) {
return true;
}

return false;
});

$delay = $metadata[0]['retryDelay'];
if (!isset($delay['seconds'])) {
$delay['seconds'] = 0;
}

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

namespace Google\Cloud\Core\Exception;

/**
* Exception thrown when a request fails due to a failed precondition.
* In REST context, this exception indicates a status code 412.
*/
class FailedPreconditionException extends ServiceException
{

}
14 changes: 12 additions & 2 deletions src/Core/Exception/ServiceException.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,26 @@ class ServiceException extends GoogleException
*/
private $serviceException;

/**
* @var array
*/
protected $options;

/**
* Handle previous exceptions differently here.
*
* @param string $message
* @param int $code
* @param Exception $serviceException
*/
public function __construct($message, $code = null, Exception $serviceException = null)
{
public function __construct(
$message,
$code = null,
Exception $serviceException = null,
array $options = []
) {
$this->serviceException = $serviceException;
$this->options = $options;

parent::__construct($message, $code);
}
Expand Down
44 changes: 42 additions & 2 deletions src/Core/GrpcRequestWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

namespace Google\Cloud\Core;

use DrSlump\Protobuf\Codec\Binary;
use DrSlump\Protobuf\Codec\CodecInterface;
use DrSlump\Protobuf\Message;
use Google\Auth\FetchAuthTokenInterface;
Expand All @@ -25,6 +26,7 @@
use Google\Cloud\Core\PhpArray;
use Google\Cloud\Core\RequestWrapperTrait;
use Google\GAX\ApiException;
use Google\GAX\OperationResponse;
use Google\GAX\PagedListResponse;
use Google\GAX\RetrySettings;
use Grpc;
Expand All @@ -47,6 +49,11 @@ class GrpcRequestWrapper
*/
private $codec;

/**
* @var CodecInterface A codec used for binary deserialization.
*/
private $binaryCodec;

/**
* @var array gRPC specific configuration options passed off to the GAX
* library.
Expand All @@ -63,6 +70,13 @@ class GrpcRequestWrapper
Grpc\STATUS_DATA_LOSS
];

/**
* @var array Map of error metadata types to RPC wrappers.
*/
private $metadataTypes = [
'google.rpc.retryinfo-bin' => \google\rpc\RetryInfo::class
];

/**
* @param array $config [optional] {
* Configuration options. Please see
Expand All @@ -88,6 +102,7 @@ public function __construct(array $config = [])
$this->authHttpHandler = $config['authHttpHandler'] ?: HttpHandlerFactory::build();
$this->codec = $config['codec'];
$this->grpcOptions = $config['grpcOptions'];
$this->binaryCodec = new Binary;
}

/**
Expand Down Expand Up @@ -134,7 +149,7 @@ public function send(callable $request, array $args, array $options = [])

try {
return $this->handleResponse($backoff->execute($request, $args));
} catch (\Exception $ex) {
} catch (ApiException $ex) {
throw $this->convertToGoogleException($ex);
}
}
Expand All @@ -155,6 +170,10 @@ private function handleResponse($response)
return $response->serialize($this->codec);
}

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

return null;
}

Expand All @@ -179,6 +198,10 @@ private function convertToGoogleException(ApiException $ex)
$exception = Exception\ConflictException::class;
break;

case Grpc\STATUS_FAILED_PRECONDITION:
$exception = Exception\FailedPreconditionException::class;
break;

case Grpc\STATUS_UNKNOWN:
$exception = Exception\ServerException::class;
break;
Expand All @@ -187,11 +210,28 @@ private function convertToGoogleException(ApiException $ex)
$exception = Exception\ServerException::class;
break;

case Grpc\STATUS_ABORTED:
$exception = Exception\AbortedException::class;
break;

default:
$exception = Exception\ServiceException::class;
break;
}

return new $exception($ex->getMessage(), $ex->getCode(), $ex);
$metadata = [];
if ($ex->getMetadata()) {
foreach ($ex->getMetadata() as $type => $binaryValue) {
if (!isset($this->metadataTypes[$type])) {
continue;
}

$metadata[] = (new $this->metadataTypes[$type])
->deserialize($binaryValue[0], $this->binaryCodec)
->serialize($this->codec);
}
}

return new $exception($ex->getMessage(), $ex->getCode(), $ex, $metadata);
}
}
20 changes: 20 additions & 0 deletions src/Core/GrpcTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,24 @@ private function formatValueForApi($value)
return ['list_value' => $this->formatListForApi($value)];
}
}

/**
* Format a timestamp for the API with nanosecond precision.
*
* @param string $value
* @return array
*/
private function formatTimestampForApi($value)
{
preg_match('/\.(\d{1,9})Z/', $value, $matches);
$value = preg_replace('/\.(\d{1,9})Z/', '.000000Z', $value);

$dt = \DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.u\Z', $value);
$nanos = (isset($matches[1])) ? $matches[1] : 0;

return [
'seconds' => (int)$dt->format('U'),
'nanos' => (int)$nanos
];
}
}
Loading

0 comments on commit ee7aa4a

Please sign in to comment.