From f28ec727933471f89024931f3c4532f1d5c31507 Mon Sep 17 00:00:00 2001 From: Phil Young Date: Wed, 29 Jul 2026 17:59:22 +0100 Subject: [PATCH 1/4] Preserve request exception context Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/Client.php | 37 ++++++++++++++++++++++++++---- src/Exception/ApiException.php | 7 +++--- src/Exception/RequestException.php | 11 +++++++++ src/Exception/UKFastException.php | 4 ++-- tests/ClientTest.php | 29 ++++++++++++++++++++++- 5 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/Exception/RequestException.php diff --git a/src/Client.php b/src/Client.php index 0f54a1e7..fcbf2458 100644 --- a/src/Client.php +++ b/src/Client.php @@ -6,6 +6,7 @@ use GuzzleHttp\Client as HttpClient; use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Exception\RequestException as GuzzleRequestException; use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Psr7\Request; use Psr\Http\Message\ResponseInterface; @@ -93,20 +94,46 @@ public function request($method, $endpoint, $body = null, $headers = []) $status = $e->getResponse()->getStatusCode(); if ($status == 404) { - throw new Exception\NotFoundException($e->getResponse()); + throw new Exception\NotFoundException($e->getResponse(), $e->getCode(), $e); } if ($status == 400 || $status == 422) { - throw new Exception\ValidationException($e->getResponse()); + throw new Exception\ValidationException($e->getResponse(), $e->getCode(), $e); } if ($status == 412) { - throw new Exception\PreconditionFailedException($e->getResponse()); + throw new Exception\PreconditionFailedException($e->getResponse(), $e->getCode(), $e); } - throw new Exception\ClientException($e->getResponse()); + throw new Exception\ClientException($e->getResponse(), $e->getCode(), $e); } catch (ServerException $e) { - throw new Exception\ServerException($e->getResponse()); + throw new Exception\ServerException($e->getResponse(), $e->getCode(), $e); + } catch (GuzzleRequestException $e) { + if ($e->hasResponse()) { + $status = $e->getResponse()->getStatusCode(); + + if ($status == 404) { + throw new Exception\NotFoundException($e->getResponse(), $e->getCode(), $e); + } + + if ($status == 400 || $status == 422) { + throw new Exception\ValidationException($e->getResponse(), $e->getCode(), $e); + } + + if ($status == 412) { + throw new Exception\PreconditionFailedException($e->getResponse(), $e->getCode(), $e); + } + + if ($status >= 500) { + throw new Exception\ServerException($e->getResponse(), $e->getCode(), $e); + } + + throw new Exception\ClientException($e->getResponse(), $e->getCode(), $e); + } + + throw new Exception\RequestException($e->getMessage(), $e->getCode(), $e); + } catch (GuzzleException $e) { + throw new Exception\RequestException($e->getMessage(), $e->getCode(), $e); } return $response; diff --git a/src/Exception/ApiException.php b/src/Exception/ApiException.php index 57ef0df1..7389601b 100644 --- a/src/Exception/ApiException.php +++ b/src/Exception/ApiException.php @@ -16,7 +16,7 @@ class ApiException extends UKFastException */ protected $response; - public function __construct($response) + public function __construct($response, $code = 0, \Exception $previous = null) { $response->getBody()->rewind(); $this->response = $response; @@ -34,14 +34,15 @@ public function __construct($response) $this->errors = $this->getApiGatewayErrorFromBody($body); } + $message = ''; if (!empty($this->errors)) { $message = $this->errors[0]->detail; if (empty($message)) { $message = $this->errors[0]->title; } - - $this->message = $message; } + + parent::__construct($message, $code, $previous); } /** diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php new file mode 100644 index 00000000..99d89c47 --- /dev/null +++ b/src/Exception/RequestException.php @@ -0,0 +1,11 @@ +paginatedRequest("/", 1, 10); } + /** + * @test + */ + public function preserves_the_original_exception_when_request_fails() + { + $request = new \GuzzleHttp\Psr7\Request('GET', '/'); + $originalException = new GuzzleRequestException('Connection failed', $request); + $mock = new MockHandler([$originalException]); + $handler = HandlerStack::create($mock); + $guzzle = new Guzzle(['handler' => $handler]); + $client = new Client($guzzle); + + $this->expectException(UKFastRequestException::class); + $this->expectExceptionMessage('Connection failed'); + + try { + $client->request('GET', '/'); + } catch (UKFastRequestException $e) { + $this->assertInstanceOf(GuzzleRequestException::class, $e->getPrevious()); + $this->assertSame($originalException, $e->getPrevious()); + $this->assertSame('Connection failed', $e->getPrevious()->getMessage()); + throw $e; + } + } + /** * @test */ From 5b06b6b34677839858f1a0a2c988fa72fa667fde Mon Sep 17 00:00:00 2001 From: Phil Young Date: Fri, 31 Jul 2026 17:05:54 +0100 Subject: [PATCH 2/4] Remove redundant code All it did wascopy the constructor from the parent Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Exception/RequestException.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Exception/RequestException.php b/src/Exception/RequestException.php index 99d89c47..48d2ce0f 100644 --- a/src/Exception/RequestException.php +++ b/src/Exception/RequestException.php @@ -4,8 +4,4 @@ class RequestException extends UKFastException { - public function __construct($message, $code = 0, \Exception $previous = null) - { - parent::__construct($message, $code, $previous); - } } From a36568f5305e21efe29bd58a7a93b5db94d68bbb Mon Sep 17 00:00:00 2001 From: Phil Young Date: Fri, 31 Jul 2026 17:08:50 +0100 Subject: [PATCH 3/4] Relax previous-exception typing for compatibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/Exception/ApiException.php | 2 +- src/Exception/UKFastException.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Exception/ApiException.php b/src/Exception/ApiException.php index 7389601b..0ef96002 100644 --- a/src/Exception/ApiException.php +++ b/src/Exception/ApiException.php @@ -16,7 +16,7 @@ class ApiException extends UKFastException */ protected $response; - public function __construct($response, $code = 0, \Exception $previous = null) + public function __construct($response, $code = 0, $previous = null) { $response->getBody()->rewind(); $this->response = $response; diff --git a/src/Exception/UKFastException.php b/src/Exception/UKFastException.php index 1f70f0d4..8b7c7573 100644 --- a/src/Exception/UKFastException.php +++ b/src/Exception/UKFastException.php @@ -6,7 +6,7 @@ class UKFastException extends RuntimeException { - public function __construct($message, $code = 0, \Exception $previous = null) + public function __construct($message, $code = 0, $previous = null) { parent::__construct($message, $code, $previous); } From b7f924be2687ad0826fa7434c2966d92f9550885 Mon Sep 17 00:00:00 2001 From: Phil Young Date: Fri, 31 Jul 2026 18:08:06 +0100 Subject: [PATCH 4/4] Correct docblocks These now match what they actually are --- src/Client.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Client.php b/src/Client.php index fcbf2458..edec3501 100644 --- a/src/Client.php +++ b/src/Client.php @@ -67,9 +67,9 @@ public function auth($token) * * @param string $method * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function request($method, $endpoint, $body = null, $headers = []) @@ -143,9 +143,9 @@ public function request($method, $endpoint, $body = null, $headers = []) * Convenience method for GET requests * * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function get($endpoint, $body = null, $headers = []) @@ -157,9 +157,9 @@ public function get($endpoint, $body = null, $headers = []) * Convenience method for POST requests * * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function post($endpoint, $body = null, $headers = []) @@ -175,9 +175,9 @@ public function post($endpoint, $body = null, $headers = []) * Convenience method for PUT requests * * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function put($endpoint, $body = null, $headers = []) @@ -193,9 +193,9 @@ public function put($endpoint, $body = null, $headers = []) * Convenience method for PATCH requests * * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function patch($endpoint, $body = null, $headers = []) @@ -211,9 +211,9 @@ public function patch($endpoint, $body = null, $headers = []) * Convenience method for DELETE requests * * @param string $endpoint - * @param string $body + * @param string|null $body * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException * @return ResponseInterface */ public function delete($endpoint, $body = null, $headers = []) @@ -233,7 +233,8 @@ public function delete($endpoint, $body = null, $headers = []) * @param int $perPage * @param array $filters * @param array $headers - * @throws GuzzleException + * @throws Exception\UKFastException + * @throws Exception\InvalidJsonException * @return Page */ public function paginatedRequest($endpoint, $page, $perPage, $filters = [], $headers = [])