From 80a66e6fe865ce04e44c2e2ebab008fea98d1ace Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Thu, 19 Jan 2023 15:31:16 +0000 Subject: [PATCH] [9.x] Add new throw helper methods to the HTTP Client (#45704) * [9.x] Added throwIfStatus method to only throw an exception if a specific status code is returned from the HTTP response * [9.x] Refactored implementation for throwIfStatus method and added throwUnlessStatus method * [9.x] Added test case for throwUnlessStatus method * [9.x] Refactored tests to follow standard of other test cases * [9.x] Added throwIfClientError and throwIfServerError methods * allow callables Co-authored-by: Taylor Otwell --- src/Illuminate/Http/Client/Response.php | 61 +++++++ src/Illuminate/Support/Facades/Http.php | 4 + tests/Http/HttpClientTest.php | 217 ++++++++++++++++++++++++ 3 files changed, 282 insertions(+) diff --git a/src/Illuminate/Http/Client/Response.php b/src/Illuminate/Http/Client/Response.php index 2e6e2d556c6c..596c5662e206 100644 --- a/src/Illuminate/Http/Client/Response.php +++ b/src/Illuminate/Http/Client/Response.php @@ -3,6 +3,7 @@ namespace Illuminate\Http\Client; use ArrayAccess; +use Closure; use Illuminate\Support\Collection; use Illuminate\Support\Traits\Macroable; use LogicException; @@ -350,6 +351,66 @@ public function throwIf($condition) return value($condition, $this) ? $this->throw(func_get_args()[1] ?? null) : $this; } + /** + * Throw an exception if the response status code matches the given code. + * + * @param callable|int $statusCode + * @return $this + * + * @throws \Illuminate\Http\Client\RequestException + */ + public function throwIfStatus($statusCode) + { + if (is_callable($statusCode) && + $statusCode($this->status(), $this)) { + return $this->throw(); + } + + return $this->status() === $statusCode ? $this->throw() : $this; + } + + /** + * Throw an exception unless the response status code matches the given code. + * + * @param callable|int $statusCode + * @return $this + * + * @throws \Illuminate\Http\Client\RequestException + */ + public function throwUnlessStatus($statusCode) + { + if (is_callable($statusCode) && + ! $statusCode($this->status(), $this)) { + return $this->throw(); + } + + return $this->status() === $statusCode ? $this : $this->throw(); + } + + /** + * Throw an exception if the response status code is a 4xx level code. + * + * @return $this + * + * @throws \Illuminate\Http\Client\RequestException + */ + public function throwIfClientError() + { + return $this->clientError() ? $this->throw() : $this; + } + + /** + * Throw an exception if the response status code is a 5xx level code. + * + * @return $this + * + * @throws \Illuminate\Http\Client\RequestException + */ + public function throwIfServerError() + { + return $this->serverError() ? $this->throw() : $this; + } + /** * Determine if the given offset exists. * diff --git a/src/Illuminate/Support/Facades/Http.php b/src/Illuminate/Support/Facades/Http.php index c35fbdbe2a37..bd0da3f5a6fc 100644 --- a/src/Illuminate/Support/Facades/Http.php +++ b/src/Illuminate/Support/Facades/Http.php @@ -59,6 +59,10 @@ * @method static \Illuminate\Http\Client\Response patch(string $url, array $data = []) * @method static \Illuminate\Http\Client\Response put(string $url, array $data = []) * @method static \Illuminate\Http\Client\Response delete(string $url, array $data = []) + * @method static \Illuminate\Http\Client\Response throwIfStatus(callable|int $statusCode) + * @method static \Illuminate\Http\Client\Response throwUnlessStatus(callable|int $statusCode) + * @method static \Illuminate\Http\Client\Response throwIfClientError() + * @method static \Illuminate\Http\Client\Response throwIfServerError() * @method static array pool(callable $callback) * @method static \Illuminate\Http\Client\Response send(string $method, string $url, array $options = []) * @method static \GuzzleHttp\Client buildClient() diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index 327e97192d73..398b1ff616b3 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -1721,6 +1721,223 @@ public function testRequestExceptionIsNotThrownIfConditionClosureIsNotSatisfied( $this->assertFalse($hitThrowCallback); } + public function testRequestExceptionIsThrownIfStatusCodeIsSatisfied() + { + $this->factory->fake([ + '*' => $this->factory::response('', 400), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api')->throwIfStatus(400); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + } + + public function testRequestExceptionIsThrownIfStatusCodeIsSatisfiedWithClosure() + { + $this->factory->fake([ + '*' => $this->factory::response('', 400), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api')->throwIfStatus(fn ($status) => $status === 400); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + } + + public function testRequestExceptionIsNotThrownIfStatusCodeIsNotSatisfied() + { + $this->factory->fake([ + '*' => $this->factory::response('', 400), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api')->throwIfStatus(500); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + } + + public function testRequestExceptionIsThrownUnlessStatusCodeIsSatisfied() + { + $this->factory->fake([ + 'http://foo.com/api/400' => $this->factory::response('', 400), + 'http://foo.com/api/408' => $this->factory::response('', 408), + 'http://foo.com/api/500' => $this->factory::response('', 500), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/400')->throwUnlessStatus(500); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + $this->factory->fake([ + 'http://foo.com/api/400' => $this->factory::response('', 400), + 'http://foo.com/api/408' => $this->factory::response('', 408), + 'http://foo.com/api/500' => $this->factory::response('', 500), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/400')->throwUnlessStatus(fn ($status) => $status === 500); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/408')->throwUnlessStatus(500); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/500')->throwUnlessStatus(500); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + } + + public function testRequestExceptionIsThrownIfIsClientError() + { + $this->factory->fake([ + 'http://foo.com/api/400' => $this->factory::response('', 400), + 'http://foo.com/api/408' => $this->factory::response('', 408), + 'http://foo.com/api/500' => $this->factory::response('', 500), + 'http://foo.com/api/504' => $this->factory::response('', 504), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/400')->throwIfClientError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/408')->throwIfClientError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/500')->throwIfClientError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/504')->throwIfClientError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + } + + public function testRequestExceptionIsThrownIfIsServerError() + { + $this->factory->fake([ + 'http://foo.com/api/400' => $this->factory::response('', 400), + 'http://foo.com/api/408' => $this->factory::response('', 408), + 'http://foo.com/api/500' => $this->factory::response('', 500), + 'http://foo.com/api/504' => $this->factory::response('', 504), + ]); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/400')->throwIfServerError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/408')->throwIfServerError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNull($exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/500')->throwIfServerError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + + $exception = null; + + try { + $this->factory->get('http://foo.com/api/504')->throwIfServerError(); + } catch (RequestException $e) { + $exception = $e; + } + + $this->assertNotNull($exception); + $this->assertInstanceOf(RequestException::class, $exception); + } + public function testItCanEnforceFaking() { $this->factory->preventStrayRequests();