From d58b5eb0221817e9ba35567f7c8db67513b92689 Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Wed, 15 Jan 2025 21:42:48 +0000 Subject: [PATCH 1/4] Return the Message error types to the ApiException class from the serializer --- src/ApiException.php | 43 ++++++++++++++++++++++++++------ src/Serializer.php | 59 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/ApiException.php b/src/ApiException.php index b66fff873..5f35a7425 100644 --- a/src/ApiException.php +++ b/src/ApiException.php @@ -46,6 +46,7 @@ class ApiException extends Exception private $metadata; private $basicMessage; private $decodedMetadataErrorInfo; + private array $protoErrors; /** * ApiException constructor. @@ -57,12 +58,14 @@ class ApiException extends Exception * @type array|null $metadata * @type string|null $basicMessage * } + * @param $protoErrors array */ public function __construct( string $message, int $code, ?string $status = null, - array $optionalArgs = [] + array $optionalArgs = [], + array $protoErrors = [], ) { $optionalArgs += [ 'previous' => null, @@ -73,6 +76,7 @@ public function __construct( $this->status = $status; $this->metadata = $optionalArgs['metadata']; $this->basicMessage = $optionalArgs['basicMessage']; + $this->protoErrors = $protoErrors; if ($this->metadata) { $this->decodedMetadataErrorInfo = self::decodeMetadataErrorInfo($this->metadata); } @@ -137,6 +141,15 @@ public function getErrorInfoMetadata() return ($this->decodedMetadataErrorInfo) ? $this->decodedMetadataErrorInfo['errorInfoMetadata'] : null; } + /** + * Returns the array containing all of the Protobuf errors + * @return array + */ + public function getProtobufErrors() + { + return $this->protoErrors; + } + /** * @param stdClass $status * @return ApiException @@ -144,11 +157,14 @@ public function getErrorInfoMetadata() public static function createFromStdClass(stdClass $status) { $metadata = property_exists($status, 'metadata') ? $status->metadata : null; + $decodedMessages = Serializer::decodeMetadata((array) $metadata); + return self::create( $status->details, $status->code, $metadata, - Serializer::decodeMetadata((array) $metadata) + $decodedMessages['serialized'], + $decodedMessages['unserialized'] ); } @@ -165,11 +181,13 @@ public static function createFromApiResponse( ?array $metadata = null, ?Exception $previous = null ) { + $decodedMessages = Serializer::decodeMetadata((array) $metadata); return self::create( $basicMessage, $rpcCode, $metadata, - Serializer::decodeMetadata((array) $metadata), + $decodedMessages['serialized'], + $decodedMessages['unserialized'], $previous ); } @@ -194,6 +212,7 @@ public static function createFromRestApiResponse( $rpcCode, $metadata, is_null($metadata) ? [] : $metadata, + Serializer::encodeMetadataToProtobufErrors($metadata ?? []), $previous ); } @@ -235,6 +254,7 @@ private static function containsErrorInfo(array $decodedMetadata) * @param int $rpcCode * @param iterable|null $metadata * @param array $decodedMetadata + * @param array $protoErrors * @param Exception|null $previous * @return ApiException */ @@ -243,6 +263,7 @@ private static function create( int $rpcCode, $metadata, array $decodedMetadata, + array $protoErrors = [], ?Exception $previous = null ) { $containsErrorInfo = self::containsErrorInfo($decodedMetadata); @@ -263,11 +284,17 @@ private static function create( $metadata = iterator_to_array($metadata); } - return new ApiException($message, $rpcCode, $rpcStatus, [ - 'previous' => $previous, - 'metadata' => $metadata, - 'basicMessage' => $basicMessage, - ]); + return new ApiException( + $message, + $rpcCode, + $rpcStatus, + [ + 'previous' => $previous, + 'metadata' => $metadata, + 'basicMessage' => $basicMessage, + ], + $protoErrors + ); } /** diff --git a/src/Serializer.php b/src/Serializer.php index 8c1047ee1..99f2f4d04 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -65,6 +65,18 @@ class Serializer 'google.rpc.localizedmessage-bin' => \Google\Rpc\LocalizedMessage::class, ]; + private static $restKnownTypes = [ + 'type.googleapis.com/google.rpc.RetryInfo' => \Google\Rpc\RetryInfo::class, + 'type.googleapis.com/google.rpc.DebugInfo' => \Google\Rpc\DebugInfo::class, + 'type.googleapis.com/google.rpc.QuotaFailure' => \Google\Rpc\QuotaFailure::class, + 'type.googleapis.com/google.rpc.BadRequest' => \Google\Rpc\BadRequest::class, + 'type.googleapis.com/google.rpc.RequestInfo' => \Google\Rpc\RequestInfo::class, + 'type.googleapis.com/google.rpc.ResourceInfo' => \Google\Rpc\ResourceInfo::class, + 'type.googleapis.com/google.rpc.ErrorInfo' => \Google\Rpc\ErrorInfo::class, + 'type.googleapis.com/google.rpc.Help' => \Google\Rpc\Help::class, + 'type.googleapis.com/google.rpc.LocalizedMessage' => \Google\Rpc\LocalizedMessage::class, + ]; + private $fieldTransformers; private $messageTypeTransformers; private $decodeFieldTransformers; @@ -130,6 +142,34 @@ public function encodeMessage($message) } } + /** + * Encode rest metadata errors to the correct protobuf Error type + * + * @param array $metadata + * @return array + */ + public static function encodeMetadataToProtobufErrors(array $metadata): array + { + $result = []; + + foreach($metadata as $error) { + $message = null; + $type = $error['@type']; + + if (!isset(self::$restKnownTypes[$type])) { + continue; + } + + $class = self::$restKnownTypes[$type]; + $message = new $class; + $jsonMessage = json_encode(array_diff($error, ['@type' => $error['@type']])); + $message->mergeFromJsonString($jsonMessage); + $result[] = $message; + } + + return $result; + } + /** * Decode PHP array into the specified protobuf message * @@ -185,16 +225,20 @@ public static function decodeMetadata(array $metadata) if (count($metadata) == 0) { return []; } - $result = []; + $result = [ + 'serialized' => [], + 'unserialized' => [] + ]; foreach ($metadata as $key => $values) { foreach ($values as $value) { $decodedValue = [ '@type' => $key, ]; + /** @var Message $message */ + $message = null; if (self::hasBinaryHeaderSuffix($key)) { if (isset(self::$metadataKnownTypes[$key])) { $class = self::$metadataKnownTypes[$key]; - /** @var Message $message */ $message = new $class(); try { $message->mergeFromString($value); @@ -207,16 +251,21 @@ public static function decodeMetadata(array $metadata) } } else { // The metadata contains an unexpected binary type + $unknownDataMessage = ''; $decodedValue += [ - 'data' => '', + 'data' => $unknownDataMessage, ]; + $message = null; } } else { $decodedValue += [ 'data' => $value, ]; } - $result[] = $decodedValue; + $result['serialized'][] = $decodedValue; + if (!is_null($message)) { + $result['unserialized'][] = $message; + } } } return $result; @@ -231,12 +280,14 @@ public static function decodeMetadata(array $metadata) public static function decodeAnyMessages($anyArray) { $results = []; + $messages = []; foreach ($anyArray as $any) { try { /** @var Any $any */ /** @var Message $unpacked */ $unpacked = $any->unpack(); $results[] = self::serializeToPhpArray($unpacked); + $messages[] = $unpacked; } catch (\Exception $ex) { echo "$ex\n"; // failed to unpack the $any object - show as unknown binary data From bbedbf6e04b63273635d3f33f4d30e04a89ec8b2 Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Thu, 16 Jan 2025 20:25:16 +0000 Subject: [PATCH 2/4] Modify the parsing logic for the Serializer --- src/ApiException.php | 12 ++++++------ src/Serializer.php | 31 ++++++++++++------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/ApiException.php b/src/ApiException.php index 5f35a7425..3bb90c27a 100644 --- a/src/ApiException.php +++ b/src/ApiException.php @@ -156,15 +156,15 @@ public function getProtobufErrors() */ public static function createFromStdClass(stdClass $status) { + $unserializedErrors = []; $metadata = property_exists($status, 'metadata') ? $status->metadata : null; - $decodedMessages = Serializer::decodeMetadata((array) $metadata); return self::create( $status->details, $status->code, $metadata, - $decodedMessages['serialized'], - $decodedMessages['unserialized'] + Serializer::decodeMetadata((array) $metadata, $unserializedErrors), + $unserializedErrors ); } @@ -181,13 +181,13 @@ public static function createFromApiResponse( ?array $metadata = null, ?Exception $previous = null ) { - $decodedMessages = Serializer::decodeMetadata((array) $metadata); + $unserializedErrors = []; return self::create( $basicMessage, $rpcCode, $metadata, - $decodedMessages['serialized'], - $decodedMessages['unserialized'], + Serializer::decodeMetadata((array) $metadata, $unserializedErrors), + $unserializedErrors, $previous ); } diff --git a/src/Serializer.php b/src/Serializer.php index 99f2f4d04..f0bd78c51 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -63,9 +63,6 @@ class Serializer 'google.rpc.errorinfo-bin' => \Google\Rpc\ErrorInfo::class, 'google.rpc.help-bin' => \Google\Rpc\Help::class, 'google.rpc.localizedmessage-bin' => \Google\Rpc\LocalizedMessage::class, - ]; - - private static $restKnownTypes = [ 'type.googleapis.com/google.rpc.RetryInfo' => \Google\Rpc\RetryInfo::class, 'type.googleapis.com/google.rpc.DebugInfo' => \Google\Rpc\DebugInfo::class, 'type.googleapis.com/google.rpc.QuotaFailure' => \Google\Rpc\QuotaFailure::class, @@ -156,11 +153,11 @@ public static function encodeMetadataToProtobufErrors(array $metadata): array $message = null; $type = $error['@type']; - if (!isset(self::$restKnownTypes[$type])) { + if (!isset(self::$metadataKnownTypes[$type])) { continue; } - $class = self::$restKnownTypes[$type]; + $class = self::$metadataKnownTypes[$type]; $message = new $class; $jsonMessage = json_encode(array_diff($error, ['@type' => $error['@type']])); $message->mergeFromJsonString($jsonMessage); @@ -218,31 +215,32 @@ public static function serializeToPhpArray(Message $message) * Decode metadata received from gRPC status object * * @param array $metadata + * @param array $unserializedErrors * @return array */ - public static function decodeMetadata(array $metadata) + public static function decodeMetadata(array $metadata, array &$unserializedErrors = null) { if (count($metadata) == 0) { return []; } - $result = [ - 'serialized' => [], - 'unserialized' => [] - ]; + $result = []; foreach ($metadata as $key => $values) { foreach ($values as $value) { $decodedValue = [ '@type' => $key, ]; - /** @var Message $message */ - $message = null; if (self::hasBinaryHeaderSuffix($key)) { if (isset(self::$metadataKnownTypes[$key])) { $class = self::$metadataKnownTypes[$key]; + /** @var Message $message */ $message = new $class(); try { $message->mergeFromString($value); $decodedValue += self::serializeToPhpArray($message); + + if (!is_null($unserializedErrors)) { + $unserializedErrors[] = $message; + } } catch (\Exception $e) { // We encountered an error trying to deserialize the data $decodedValue += [ @@ -251,21 +249,16 @@ public static function decodeMetadata(array $metadata) } } else { // The metadata contains an unexpected binary type - $unknownDataMessage = ''; $decodedValue += [ - 'data' => $unknownDataMessage, + 'data' => '', ]; - $message = null; } } else { $decodedValue += [ 'data' => $value, ]; } - $result['serialized'][] = $decodedValue; - if (!is_null($message)) { - $result['unserialized'][] = $message; - } + $result[] = $decodedValue; } } return $result; From 60da4f91ed9b937846a6fb3c016191154d3c398f Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Fri, 17 Jan 2025 22:51:01 +0000 Subject: [PATCH 3/4] Remove unnecessary message array --- src/Serializer.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Serializer.php b/src/Serializer.php index f0bd78c51..f0f9c55c8 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -273,14 +273,12 @@ public static function decodeMetadata(array $metadata, array &$unserializedError public static function decodeAnyMessages($anyArray) { $results = []; - $messages = []; foreach ($anyArray as $any) { try { /** @var Any $any */ /** @var Message $unpacked */ $unpacked = $any->unpack(); $results[] = self::serializeToPhpArray($unpacked); - $messages[] = $unpacked; } catch (\Exception $ex) { echo "$ex\n"; // failed to unpack the $any object - show as unknown binary data From 2a2549522361b7bb8ad62589175de28a243a63d8 Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Fri, 17 Jan 2025 22:54:08 +0000 Subject: [PATCH 4/4] Import the Message type --- src/ApiException.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ApiException.php b/src/ApiException.php index 3bb90c27a..fdb32322c 100644 --- a/src/ApiException.php +++ b/src/ApiException.php @@ -32,6 +32,7 @@ namespace Google\ApiCore; use Exception; +use Google\Protobuf\Internal\Message; use Google\Protobuf\Internal\RepeatedField; use Google\Rpc\Status; use GuzzleHttp\Exception\RequestException;