Skip to content

Commit

Permalink
[9.x] Add new throw helper methods to the HTTP Client (#45704)
Browse files Browse the repository at this point in the history
* [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 <taylor@laravel.com>
  • Loading branch information
WendellAdriel and taylorotwell authored Jan 19, 2023
1 parent 53ab77d commit 80a66e6
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/Illuminate/Http/Client/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Http\Client;

use ArrayAccess;
use Closure;
use Illuminate\Support\Collection;
use Illuminate\Support\Traits\Macroable;
use LogicException;
Expand Down Expand Up @@ -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.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Illuminate/Support/Facades/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
217 changes: 217 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 80a66e6

Please sign in to comment.