Skip to content

Commit

Permalink
[9.x] Http client - allow to provide closure as "throwif" condition (#…
Browse files Browse the repository at this point in the history
…45251)

* Option to use closure as throwif conditon

* Fix facade dockblock

* add consistency to throw and throw if

* update tests

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
gdebrauwer and taylorotwell committed Dec 9, 2022
1 parent 1544f6f commit 26c7029
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 6 deletions.
20 changes: 17 additions & 3 deletions src/Illuminate/Http/Client/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ class PendingRequest
*/
protected $throwCallback;

/**
* A callback to check if an exception should be thrown when a server or client error occurs.
*
* @var \Closure
*/
protected $throwIfCallback;

/**
* The number of times to try the request.
*
Expand Down Expand Up @@ -599,12 +606,17 @@ public function throw(callable $callback = null)
/**
* Throw an exception if a server or client error occurred and the given condition evaluates to true.
*
* @param bool $condition
* @param callable|bool $condition
* @param callable|null $throwCallback
* @return $this
*/
public function throwIf($condition)
{
return $condition ? $this->throw() : $this;
if (is_callable($condition)) {
$this->throwIfCallback = $condition;
}

return $condition ? $this->throw(func_get_args()[1] ?? null) : $this;
}

/**
Expand Down Expand Up @@ -797,7 +809,9 @@ public function send(string $method, string $url, array $options = [])
throw $exception;
}

if ($this->throwCallback) {
if ($this->throwCallback &&
($this->throwIfCallback === null ||
call_user_func($this->throwIfCallback, $response))) {
$response->throw($this->throwCallback);
}

Expand Down
5 changes: 3 additions & 2 deletions src/Illuminate/Http/Client/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,15 @@ public function throw()
/**
* Throw an exception if a server or client error occurred and the given condition evaluates to true.
*
* @param bool $condition
* @param \Closure|bool $condition
* @param \Closure|null $throwCallback
* @return $this
*
* @throws \Illuminate\Http\Client\RequestException
*/
public function throwIf($condition)
{
return $condition ? $this->throw() : $this;
return value($condition, $this) ? $this->throw(func_get_args()[1] ?? null) : $this;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Support/Facades/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
* @method static \Illuminate\Http\Client\PendingRequest withMiddleware(callable $middleware)
* @method static \Illuminate\Http\Client\PendingRequest beforeSending(callable $callback)
* @method static \Illuminate\Http\Client\PendingRequest throw(callable|null $callback = null)
* @method static \Illuminate\Http\Client\PendingRequest throwIf(bool $condition)
* @method static \Illuminate\Http\Client\PendingRequest throwIf(callable|bool $condition, callable|null $throwCallback)
* @method static \Illuminate\Http\Client\PendingRequest throwUnless(bool $condition)
* @method static \Illuminate\Http\Client\PendingRequest dump()
* @method static \Illuminate\Http\Client\PendingRequest dd()
Expand Down
107 changes: 107 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,63 @@ public function testRequestExceptionIsNotThrownIfTheThrowIfOnThePendingRequestIs
$this->assertSame(403, $response->status());
}

public function testRequestExceptionIsThrownIfTheThrowIfClosureOnThePendingRequestReturnsTrue()
{
$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$exception = null;

$hitThrowCallback = false;

try {
$this->factory
->throwIf(function ($response) {
$this->assertInstanceOf(Response::class, $response);
$this->assertSame(403, $response->status());

return true;
}, function ($response, $e) use (&$hitThrowCallback) {
$this->assertInstanceOf(Response::class, $response);
$this->assertSame(403, $response->status());

$this->assertInstanceOf(RequestException::class, $e);
$hitThrowCallback = true;
})
->get('http://foo.com/get');
} catch (RequestException $e) {
$exception = $e;
}

$this->assertNotNull($exception);
$this->assertInstanceOf(RequestException::class, $exception);
$this->assertTrue($hitThrowCallback);
}

public function testRequestExceptionIsNotThrownIfTheThrowIfClosureOnThePendingRequestReturnsFalse()
{
$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$hitThrowCallback = false;

$response = $this->factory
->throwIf(function ($response) {
$this->assertInstanceOf(Response::class, $response);
$this->assertSame(403, $response->status());

return false;
}, function ($response, $e) use (&$hitThrowCallback) {
$hitThrowCallback = true;
})
->get('http://foo.com/get');

$this->assertSame(403, $response->status());
$this->assertFalse($hitThrowCallback);
}

public function testRequestExceptionIsThrownWithCallbackIfThePendingRequestIsSetToThrowOnFailure()
{
$this->factory->fake([
Expand Down Expand Up @@ -1603,6 +1660,56 @@ public function testRequestExceptionIsNotThrownIfConditionIsNotSatisfied()
$this->assertSame('{"result":{"foo":"bar"}}', $response->body());
}

public function testRequestExceptionIsThrowIfConditionClosureIsSatisfied()
{
$this->factory->fake([
'*' => $this->factory::response('', 400),
]);

$exception = null;

$hitThrowCallback = false;

try {
$this->factory->get('http://foo.com/api')->throwIf(function ($response) {
$this->assertSame(400, $response->status());

return true;
}, function ($response, $e) use (&$hitThrowCallback) {
$this->assertSame(400, $response->status());
$this->assertInstanceOf(RequestException::class, $e);

$hitThrowCallback = true;
});
} catch (RequestException $e) {
$exception = $e;
}

$this->assertNotNull($exception);
$this->assertInstanceOf(RequestException::class, $exception);
$this->assertTrue($hitThrowCallback);
}

public function testRequestExceptionIsNotThrownIfConditionClosureIsNotSatisfied()
{
$this->factory->fake([
'*' => $this->factory::response(['result' => ['foo' => 'bar']], 400),
]);

$hitThrowCallback = false;

$response = $this->factory->get('http://foo.com/api')->throwIf(function ($response) {
$this->assertSame(400, $response->status());

return false;
}, function ($response, $e) use (&$hitThrowCallback) {
$hitThrowCallback = true;
});

$this->assertSame('{"result":{"foo":"bar"}}', $response->body());
$this->assertFalse($hitThrowCallback);
}

public function testItCanEnforceFaking()
{
$this->factory->preventStrayRequests();
Expand Down

0 comments on commit 26c7029

Please sign in to comment.