Skip to content

Commit

Permalink
ISSUE-31: Add Middlewares to HandlerStack (#37)
Browse files Browse the repository at this point in the history
* ISSUE-31: Add Middlewares to HandlerStack

* ClientBuilder moved inside Guzzle namespace

* FIX

---------

Co-authored-by: Fabrizio Gargiulo <[email protected]>
  • Loading branch information
doppiogancio and Fabrizio Gargiulo authored Jun 9, 2023
1 parent 2ecde9b commit 2a6cda0
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 76 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ $ composer require doppiogancio/mocked-client guzzlehttp/guzzle php-http/discove
This version requires a minimum PHP version 8.1

## How to mock a client

```php
use DoppioGancio\MockedClient\HandlerBuilder;
use DoppioGancio\MockedClient\MockedGuzzleClientBuilder;
use DoppioGancio\MockedClient\ClientBuilder;
use DoppioGancio\MockedClient\Route\RouteBuilder;
use GuzzleHttp\Psr7\Response;
use Http\Discovery\Psr17FactoryDiscovery;
Expand Down Expand Up @@ -70,7 +71,7 @@ $handlerBuilder->addRoute(
->build()
);

$clientBuilder = new MockedGuzzleClientBuilder($handlerBuilder, new NullLogger());
$clientBuilder = new ClientBuilder($handlerBuilder, new NullLogger());
$client = $clientBuilder->build();
```

Expand Down Expand Up @@ -102,7 +103,7 @@ $handlerBuilder = new HandlerBuilder(

// don't add any route for now...

$clientBuilder = new MockedGuzzleClientBuilder($handlerBuilder);
$clientBuilder = new ClientBuilder($handlerBuilder);
$client = $clientBuilder->build();
```

Expand Down
42 changes: 42 additions & 0 deletions src/MockedClient/Guzzle/ClientBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace DoppioGancio\MockedClient\Guzzle;

use DoppioGancio\MockedClient\HandlerBuilder;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

class ClientBuilder
{
/** @param array<callable> $middlewares */
public function __construct(
private readonly HandlerBuilder $handlerBuilder,
private array $middlewares = [],
) {
}

public function addMiddleware(callable $middleware): self
{
$this->middlewares[] = $middleware;

return $this;
}

/** @param array<string,mixed> $options */
public function build(array $options = []): Client
{
$handler = $this->handlerBuilder->build();

$handlerStack = HandlerStack::create($handler);

foreach ($this->middlewares as $middleware) {
$handlerStack->push($middleware);
}

$options['handler'] = $handlerStack;

return new Client($options);
}
}
34 changes: 34 additions & 0 deletions src/MockedClient/Guzzle/Middleware/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace DoppioGancio\MockedClient\Guzzle\Middleware;

use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\RequestInterface;

class Middleware
{
protected RequestInterface $request;

public function __invoke(callable $handler): callable
{
return function (RequestInterface $request, array $options) use ($handler) {
$this->request = $this->mapRequest($request);

$response = $handler($this->request, $options);

return $this->mapResponse($response);
};
}

protected function mapRequest(RequestInterface $request): RequestInterface
{
return $request;
}

protected function mapResponse(PromiseInterface $response): PromiseInterface
{
return $response;
}
}
7 changes: 4 additions & 3 deletions src/MockedClient/HandlerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
use Closure;
use DoppioGancio\MockedClient\Exception\RouteNotFound;
use DoppioGancio\MockedClient\Route\Route;
use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\PromiseInterface;
use League\Route\Http\Exception\NotFoundException;
use League\Route\Router;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Log\LoggerInterface;
use Throwable;
Expand Down Expand Up @@ -38,7 +39,7 @@ public function addRoute(Route $route): self

public function build(): Closure
{
return function (RequestInterface $request): ResponseInterface {
return function (RequestInterface $request): PromiseInterface {
$router = new Router();
foreach ($this->routes as $route) {
$router->map(
Expand Down Expand Up @@ -79,7 +80,7 @@ public function build(): Closure
],
);

return $response;
return new FulfilledPromise($response);
} catch (NotFoundException $e) {
$this->logError($e, $request);

Expand Down
64 changes: 0 additions & 64 deletions src/MockedClient/MockedGuzzleClientBuilder.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

declare(strict_types=1);

namespace DoppioGancio\MockedClient\Tests;
namespace DoppioGancio\MockedClient\Tests\Guzzle;

use DoppioGancio\MockedClient\Guzzle\ClientBuilder;
use DoppioGancio\MockedClient\Guzzle\Middleware\Middleware;
use DoppioGancio\MockedClient\HandlerBuilder;
use DoppioGancio\MockedClient\MockedGuzzleClientBuilder;
use DoppioGancio\MockedClient\Route\ConditionalRouteBuilder;
use DoppioGancio\MockedClient\Route\RouteBuilder;
use GuzzleHttp\Client;
Expand All @@ -16,11 +17,12 @@
use GuzzleHttp\Psr7\Response;
use Http\Discovery\Psr17FactoryDiscovery;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use Psr\Log\NullLogger;

use function json_decode;

class HandlerStackBuilderTest extends TestCase
class ClientBuilderTest extends TestCase
{
private HandlerBuilder $handlerBuilder;
private RouteBuilder $routeBuilder;
Expand Down Expand Up @@ -167,6 +169,13 @@ public function testRequestWithFormBody(): void
$this->assertEquals('key1=value1&key2=value2', $body);
}

public function testMiddlewares(): void
{
$response = $this->getMockedClient()->request('GET', '/middleware');
$body = (string) $response->getBody();
$this->assertEquals('x-value', $body);
}

private function getMockedClient(): Client
{
$this->handlerBuilder->addRoute(
Expand Down Expand Up @@ -240,7 +249,29 @@ private function getMockedClient(): Client
->build(),
);

$clientBuilder = new MockedGuzzleClientBuilder($this->handlerBuilder, new NullLogger());
$this->handlerBuilder->addRoute(
$this->routeBuilder->new()
->withMethod('GET')
->withPath('/middleware')
->withHandler(static function (Request $request): Response {
return new Response(200, [], $request->getHeader('x-name')[0]);
})
->build(),
);

$clientBuilder = new ClientBuilder($this->handlerBuilder);

// Anonymous middleware
$clientBuilder->addMiddleware(new class ('x-name', 'x-value') extends Middleware {
public function __construct(private readonly string $header, private readonly string $value)
{
}

protected function mapRequest(RequestInterface $request): RequestInterface
{
return $request->withHeader($this->header, $this->value);
}
});

return $clientBuilder->build();
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions tests/Route/ConditionalRouteBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function testDefaultRouteNotFound(): void
->withConditionalResponse('page=2&code=it', new Response(201))
->withConditionalResponse('code=de', new Response(301))
->withConditionalResponse('page=4', new Response(401))
->withDefaultFileResponse(__DIR__ . '/../fixtures/countries.json')
->withDefaultFileResponse(__DIR__ . '/fixtures/countries.json')
->build();

$response = $route->getHandler()(new Request('GET', '/country?nonce=12345&code=fr&page=3'));
Expand All @@ -72,7 +72,7 @@ public function testQueryStringWithArrayNotation(): void
)
->withConditionalFileResponse(
'filters%5BhasContent%5D=1',
__DIR__ . '/../fixtures/api_parts_manufacturers.json',
__DIR__ . '/fixtures/api_parts_manufacturers.json',
)
->build();

Expand Down
18 changes: 18 additions & 0 deletions tests/Route/fixtures/api_parts_manufacturers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"manufacturers": [
{
"value": "a-1111",
"label": "Manufacturer #1, Inc.",
"count": 12,
"categoryCount": 4,
"partsWithOffersCount": 1111
},
{
"value": "b-222",
"label": "Manufacturer #2, Inc.",
"count": 22,
"categoryCount": 5,
"partsWithOffersCount": 2222
}
]
}
12 changes: 12 additions & 0 deletions tests/Route/fixtures/countries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"id": "+39",
"code": "IT",
"name": "Italy"
},
{
"id": "+49",
"code": "DE",
"name": "Germany"
}
]

0 comments on commit 2a6cda0

Please sign in to comment.