Skip to content

Commit

Permalink
Integrate Laravel HTTP client events
Browse files Browse the repository at this point in the history
  • Loading branch information
cerbero90 committed Feb 11, 2024
1 parent dfdf383 commit 053c419
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 4 deletions.
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"require-dev": {
"illuminate/http": ">=6.20",
"illuminate/support": ">=6.20",
"mockery/mockery": "^1.3.4",
"orchestra/testbench": ">=7.0",
"pestphp/pest": "^2.0",
Expand Down Expand Up @@ -55,6 +56,11 @@
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
},
"laravel": {
"providers": [
"Cerbero\\LazyJsonPages\\Providers\\LazyJsonPagesServiceProvider"
]
}
},
"config": {
Expand Down
52 changes: 52 additions & 0 deletions src/Providers/LazyJsonPagesServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Cerbero\LazyJsonPages\Providers;

use Cerbero\LazyJsonPages\Services\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use Illuminate\Http\Client\Events\ConnectionFailed;
use Illuminate\Http\Client\Events\RequestSending;
use Illuminate\Http\Client\Events\ResponseReceived;
use Illuminate\Http\Client\Request;
use Illuminate\Http\Client\Response;
use Illuminate\Support\ServiceProvider;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* The service provider to integrate with Laravel.
*/
final class LazyJsonPagesServiceProvider extends ServiceProvider
{
/**
* Bootstrap the services.
*/
public function boot(): void
{
Client::middleware('laravel_events', Middleware::tap($this->sending(...), $this->sent(...)));
}

/**
* Handle HTTP requests before they are sent.
*/
private function sending(RequestInterface $request): void
{
event(new RequestSending(new Request($request)));
}

/**
* Handle HTTP requests after they are sent.
*/
private function sent(RequestInterface $request, array $options, PromiseInterface $promise): void
{
$clientRequest = new Request($request);

$promise->then(
fn(ResponseInterface $response) => event(new ResponseReceived($clientRequest, new Response($response))),
fn() => event(new ConnectionFailed($clientRequest)),
);
}
}
34 changes: 31 additions & 3 deletions src/Services/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Cerbero\LazyJsonPages\Services;

use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\RequestOptions;

/**
Expand All @@ -27,9 +28,18 @@ final class Client

/**
* The custom options.
*
* @var array<string, mixed>
*/
private static array $options = [];

/**
* The client middleware.
*
* @var array<string, callable>
*/
private static array $middleware = [];

/**
* The Guzzle client instance.
*/
Expand All @@ -40,9 +50,18 @@ final class Client
*/
public static function instance(): Guzzle
{
return self::$guzzle ??= new Guzzle(
array_replace_recursive(self::$defaultOptions, self::$options),
);
if (self::$guzzle) {
return self::$guzzle;
}

$options = array_replace_recursive(self::$defaultOptions, self::$options);
$options['handler'] ??= HandlerStack::create();

foreach (self::$middleware as $name => $middleware) {
$options['handler']->push($middleware, $name);
}

return self::$guzzle = new Guzzle($options);
}

/**
Expand All @@ -53,13 +72,22 @@ public static function configure(array $options): void
self::$options = array_replace_recursive(self::$options, $options);
}

/**
* Set the Guzzle client middleware.
*/
public static function middleware(string $name, callable $middleware): void
{
self::$middleware[$name] = $middleware;
}

/**
* Clean up the static values.
*/
public static function reset(): void
{
self::$guzzle = null;
self::$options = [];
self::$middleware = [];
}

/**
Expand Down
37 changes: 37 additions & 0 deletions tests/Integration/LaravelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

use Cerbero\LazyJsonPages\LazyJsonPages;
use Illuminate\Http\Client\Events\ConnectionFailed;
use Illuminate\Http\Client\Events\RequestSending;
use Illuminate\Http\Client\Events\ResponseReceived;
use Illuminate\Support\Facades\Event;

it('fires Laravel HTTP client events on success', function() {
Event::fake();

$lazyCollection = LazyJsonPages::from('https://example.com/api/v1/users')
->totalPages('meta.total_pages')
->collect('data.*');

expect($lazyCollection)->toLoadItemsViaRequests([
'https://example.com/api/v1/users' => 'pagination/page1.json',
'https://example.com/api/v1/users?page=2' => 'pagination/page2.json',
'https://example.com/api/v1/users?page=3' => 'pagination/page3.json',
]);

Event::assertDispatched(RequestSending::class, 3);
Event::assertDispatched(ResponseReceived::class, 3);
});

it('fires Laravel HTTP client events on failure', function() {
Event::fake();

$lazyCollection = LazyJsonPages::from('https://example.com/api/v1/users')
->totalPages('meta.total_pages')
->collect('data.*');

expect($lazyCollection)->toFailRequest('https://example.com/api/v1/users');

Event::assertDispatched(RequestSending::class, 1);
Event::assertDispatched(ConnectionFailed::class, 1);
});
27 changes: 26 additions & 1 deletion tests/Pest.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<?php

use Cerbero\LazyJsonPages\Services\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as OrchestraTestCase;

/*
|--------------------------------------------------------------------------
Expand All @@ -17,7 +21,7 @@
|
*/

// uses(Tests\TestCase::class)->in('Feature');
uses(OrchestraTestCase::class, WithWorkbench::class)->in('Integration/LaravelTest.php');

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -57,6 +61,27 @@
expect($actualUris)->toBe($expectedUris);
});

expect()->extend('toFailRequest', function (string $uri) {
$transactions = [];

$responses = [$exception = new RequestException('connection failed', new Request('GET', $uri))];

$stack = HandlerStack::create(new MockHandler($responses));

$stack->push(Middleware::history($transactions));

Client::configure(['handler' => $stack]);

try {
iterator_to_array($this->value);
} catch (Throwable $e) {
expect($e)->toBe($exception);
}

expect($transactions)->toHaveCount(1);
expect((string) $transactions[0]['request']->getUri())->toBe($uri);
});

/*
|--------------------------------------------------------------------------
| Functions
Expand Down

0 comments on commit 053c419

Please sign in to comment.