| 1 |
<?php |
| 2 |
|
| 3 |
namespace Dudlewebs\WPMCS\s3\Aws\Api\ErrorParser; |
| 4 |
|
| 5 |
use Dudlewebs\WPMCS\s3\Aws\Api\Parser\PayloadParserTrait; |
| 6 |
use Dudlewebs\WPMCS\s3\Aws\Api\StructureShape; |
| 7 |
use Dudlewebs\WPMCS\s3\Psr\Http\Message\ResponseInterface; |
| 8 |
/** |
| 9 |
* Provides basic JSON error parsing functionality. |
| 10 |
*/ |
| 11 |
trait JsonParserTrait |
| 12 |
{ |
| 13 |
use PayloadParserTrait; |
| 14 |
private function genericHandler(ResponseInterface $response) : array |
| 15 |
{ |
| 16 |
$code = (string) $response->getStatusCode(); |
| 17 |
$error_code = null; |
| 18 |
$error_type = null; |
| 19 |
// Parse error code and type for query compatible services |
| 20 |
if ($this->api && !\is_null($this->api->getMetadata('awsQueryCompatible')) && $response->hasHeader('x-amzn-query-error')) { |
| 21 |
$awsQueryError = $this->parseAwsQueryCompatibleHeader($response); |
| 22 |
if ($awsQueryError) { |
| 23 |
$error_code = $awsQueryError['code']; |
| 24 |
$error_type = $awsQueryError['type']; |
| 25 |
} |
| 26 |
} |
| 27 |
// Parse error code from X-Amzn-Errortype header |
| 28 |
if (!$error_code && $response->hasHeader('X-Amzn-Errortype')) { |
| 29 |
$error_code = $this->extractErrorCode($response->getHeaderLine('X-Amzn-Errortype')); |
| 30 |
} |
| 31 |
$parsedBody = null; |
| 32 |
$body = $response->getBody(); |
| 33 |
if (!$body->isSeekable() || $body->getSize()) { |
| 34 |
$parsedBody = $this->parseJson((string) $body, $response); |
| 35 |
} |
| 36 |
// Parse error code from response body |
| 37 |
if (!$error_code && $parsedBody) { |
| 38 |
$error_code = $this->parseErrorFromBody($parsedBody); |
| 39 |
} |
| 40 |
if (!isset($error_type)) { |
| 41 |
$error_type = $code[0] == '4' ? 'client' : 'server'; |
| 42 |
} |
| 43 |
return ['request_id' => $response->getHeaderLine('x-amzn-requestid'), 'code' => $error_code ?? null, 'message' => null, 'type' => $error_type, 'parsed' => $parsedBody]; |
| 44 |
} |
| 45 |
/** |
| 46 |
* Parse AWS Query Compatible error from header |
| 47 |
* |
| 48 |
* @param ResponseInterface $response |
| 49 |
* @return array|null Returns ['code' => string, 'type' => string] or null |
| 50 |
*/ |
| 51 |
private function parseAwsQueryCompatibleHeader(ResponseInterface $response) : ?array |
| 52 |
{ |
| 53 |
$queryError = $response->getHeaderLine('x-amzn-query-error'); |
| 54 |
$parts = \explode(';', $queryError); |
| 55 |
if (\count($parts) === 2 && $parts[0] && $parts[1]) { |
| 56 |
return ['code' => $parts[0], 'type' => $parts[1]]; |
| 57 |
} |
| 58 |
return null; |
| 59 |
} |
| 60 |
/** |
| 61 |
* Parse error code from response body |
| 62 |
* |
| 63 |
* @param array|null $parsedBody |
| 64 |
* @return string|null |
| 65 |
*/ |
| 66 |
private function parseErrorFromBody(?array $parsedBody) : ?string |
| 67 |
{ |
| 68 |
if (!$parsedBody || !isset($parsedBody['code']) && !isset($parsedBody['__type'])) { |
| 69 |
return null; |
| 70 |
} |
| 71 |
$error_code = $parsedBody['code'] ?? $parsedBody['__type']; |
| 72 |
return $this->extractErrorCode($error_code); |
| 73 |
} |
| 74 |
/** |
| 75 |
* Extract error code from raw error string containing # and/or : delimiters |
| 76 |
* |
| 77 |
* @param string $rawErrorCode |
| 78 |
* @return string |
| 79 |
*/ |
| 80 |
private function extractErrorCode(string $rawErrorCode) : string |
| 81 |
{ |
| 82 |
// Handle format with both # and uri (e.g., "namespace#http://foo-bar") |
| 83 |
if (\str_contains($rawErrorCode, ':') && \str_contains($rawErrorCode, '#')) { |
| 84 |
$start = \strpos($rawErrorCode, '#') + 1; |
| 85 |
$end = \strpos($rawErrorCode, ':', $start); |
| 86 |
return \substr($rawErrorCode, $start, $end - $start); |
| 87 |
} |
| 88 |
// Handle format with uri only : (e.g., "ErrorCode:http://foo-bar.com/baz") |
| 89 |
if (\str_contains($rawErrorCode, ':')) { |
| 90 |
return \substr($rawErrorCode, 0, \strpos($rawErrorCode, ':')); |
| 91 |
} |
| 92 |
// Handle format with only # (e.g., "namespace#ErrorCode") |
| 93 |
if (\str_contains($rawErrorCode, '#')) { |
| 94 |
return \substr($rawErrorCode, \strpos($rawErrorCode, '#') + 1); |
| 95 |
} |
| 96 |
return $rawErrorCode; |
| 97 |
} |
| 98 |
protected function payload(ResponseInterface $response, StructureShape $member) |
| 99 |
{ |
| 100 |
$body = $response->getBody(); |
| 101 |
if (!$body->isSeekable() || $body->getSize()) { |
| 102 |
$jsonBody = $this->parseJson($body, $response); |
| 103 |
} else { |
| 104 |
$jsonBody = (string) $body; |
| 105 |
} |
| 106 |
return $this->parser->parse($member, $jsonBody); |
| 107 |
} |
| 108 |
} |
| 109 |
|