Skip to content

Commit

Permalink
Merge pull request #9 from mops1k/1.0.8
Browse files Browse the repository at this point in the history
1.0.8
  • Loading branch information
mops1k authored Jan 7, 2024
2 parents cacaa6b + 2d6e4d7 commit 1556139
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 3 deletions.
9 changes: 8 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use ApiClientBundle\Builder\RequestUriBuilder;
use ApiClientBundle\HTTP\Context\ContextStorage;
use ApiClientBundle\HTTP\Context\ContextStorageInterface;
use ApiClientBundle\HTTP\HttpClient;
use ApiClientBundle\HTTP\HttpClientInterface;
use ApiClientBundle\Serializer\ListResponseDenormalizer;
Expand All @@ -19,14 +21,19 @@
->tag('api.http_client.plugin')
;

$services->set(ContextStorageInterface::class)
->class(ContextStorage::class)
;

$services->set(HttpClientInterface::class)
->class(HttpClient::class)
->arg('$serializer', service(SerializerInterface::class))
->arg('$container', service('service_container'))
->arg('$contextStorage', service(ContextStorageInterface::class))
->arg('$plugins', tagged_iterator('api.http_client.plugin'))
->public()
;

$services->set(ListResponseDenormalizer::class)
->tag('serializer.normalizer');
->tag('serializer.normalizer');
};
25 changes: 25 additions & 0 deletions src/HTTP/Context/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace ApiClientBundle\HTTP\Context;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class Context implements ContextInterface
{
public function __construct(
private readonly RequestInterface $request,
private readonly ?ResponseInterface $response,
) {
}

public function getRequest(): RequestInterface
{
return $this->request;
}

public function getResponse(): ?ResponseInterface
{
return $this->response;
}
}
14 changes: 14 additions & 0 deletions src/HTTP/Context/ContextInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace ApiClientBundle\HTTP\Context;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface ContextInterface
{
public function __construct(RequestInterface $request, ?ResponseInterface $response);

public function getRequest(): RequestInterface;
public function getResponse(): ?ResponseInterface;
}
51 changes: 51 additions & 0 deletions src/HTTP/Context/ContextStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace ApiClientBundle\HTTP\Context;

use ApiClientBundle\Client\QueryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class ContextStorage implements ContextStorageInterface
{
/**
* @var array<string, ContextInterface>
*/
private static array $collection = [];

public function add(QueryInterface $query, RequestInterface $request, ?ResponseInterface $response = null): static
{
self::$collection[\spl_object_hash($query)] = new Context(
request: $request,
response: $response
);

return $this;
}

public static function get(QueryInterface $query): ?ContextInterface
{
if (false === self::has($query)) {
return null;
}

return self::$collection[\spl_object_hash($query)];
}

public static function remove(QueryInterface $query): void
{
if (true === self::has($query)) {
unset(self::$collection[\spl_object_hash($query)]);
}
}

public static function has(QueryInterface $query): bool
{
return \array_key_exists(\spl_object_hash($query), self::$collection);
}

public static function clear(): void
{
self::$collection = [];
}
}
16 changes: 16 additions & 0 deletions src/HTTP/Context/ContextStorageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace ApiClientBundle\HTTP\Context;

use ApiClientBundle\Client\QueryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface ContextStorageInterface
{
public function add(QueryInterface $query, RequestInterface $request, ?ResponseInterface $response): static;
public static function get(QueryInterface $query): ?ContextInterface;
public static function remove(QueryInterface $query): void;
public static function has(QueryInterface $query): bool;
public static function clear(): void;
}
13 changes: 11 additions & 2 deletions src/HTTP/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use ApiClientBundle\Client\ServiceInterface;
use ApiClientBundle\Exception\HttpRequestException;
use ApiClientBundle\Exception\ServerErrorException;
use ApiClientBundle\HTTP\Context\ContextStorageInterface;
use Http\Client\Common\Exception\ClientErrorException;
use Http\Client\Common\Exception\ServerErrorException as BaseServerErrorException;
use Http\Client\Common\Plugin;
Expand All @@ -27,15 +28,17 @@ final class HttpClient implements HttpClientInterface

public function __construct(
private readonly SerializerInterface $serializer,
protected ContainerInterface $container,
private readonly ContainerInterface $container,
private readonly ContextStorageInterface $contextStorage,
/**
* @var \Traversable<Plugin>|null
*/
protected readonly ?\Traversable $plugins = null,
private readonly ?\Traversable $plugins = null,
private ?ClientInterface $client = null,
protected RequestBodyBuilder $bodyBuilder = new RequestBodyBuilder(),
) {
$this->requestFactory = Psr17FactoryDiscovery::findRequestFactory();
$this->contextStorage::clear();
}

/**
Expand Down Expand Up @@ -99,6 +102,12 @@ public function request(QueryInterface $query): ResponseInterface
request: $request,
previous: $e
);
} finally {
$this->contextStorage->add(
query: $query,
request: $request,
response: $psr17Response ?? null
);
}
}

Expand Down
55 changes: 55 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
use ApiClientBundle\Enum\HttpResponseStatusEnum;
use ApiClientBundle\Exception\HttpRequestException;
use ApiClientBundle\Exception\ServerErrorException;
use ApiClientBundle\HTTP\Context\ContextStorage;
use ApiClientBundle\HTTP\HttpClient;
use ApiClientBundle\HTTP\HttpClientInterface;
use ApiClientBundle\HTTP\HttpNetworkException;
use ApiClientBundle\Tests\Mock\ListQuery;
use ApiClientBundle\Tests\Mock\ListResponse;
use ApiClientBundle\Tests\Mock\Query;
Expand All @@ -16,6 +18,7 @@
use ApiClientBundle\Tests\Mock\ResponseWithFile;
use ApiClientBundle\Tests\Stubs\Kernel;
use GuzzleHttp\Psr7\Response as HttpResponse;
use Http\Client\Exception\NetworkException;
use Http\Discovery\Psr17Factory;
use Http\Discovery\Psr18ClientDiscovery;
use Http\Discovery\Strategy\MockClientStrategy;
Expand Down Expand Up @@ -70,6 +73,41 @@ public function testHttpClientRequestSuccess(): void
self::assertEquals('Ok!', $response->status);
}

public function testHttpClientContextWithRequestSuccess(): void
{
$mockResponseContents = \file_get_contents(__DIR__ . '/Stubs/Response/ok.json');
$mockResponse = $this->createMock(HttpResponse::class);

$requestMatcher = new RequestMatcher();
$builtRequest = $builtResponse = null;
$this->mockHttpClient->on(
$requestMatcher,
function (RequestInterface $request) use ($mockResponseContents, $mockResponse, &$builtRequest, &$builtResponse) {
$mockResponse->method('getStatusCode')->willReturn(HttpResponseStatusEnum::STATUS_200->getCode());
$streamMock = (new Psr17Factory())->createStream($mockResponseContents);
$mockResponse->method('getBody')->willReturn($streamMock);

$builtRequest = $request;
$builtResponse = $mockResponse;

return $mockResponse;
}
);

$query = new Query();
/** @var Response $response */
$response = $this->client->request($query);
self::assertEquals('Ok!', $response->status);
self::assertSame('Ok!', $response->status);

$context = ContextStorage::get($query);
self::assertSame($builtRequest, $context->getRequest());
self::assertSame($builtResponse, $context->getResponse());

ContextStorage::clear();
self::assertNull(ContextStorage::get($query));
}

public function testHttpClientCollectionRequestSuccess(): void
{
$mockResponseContents = \file_get_contents(__DIR__ . '/Stubs/Response/collection.json');
Expand Down Expand Up @@ -158,4 +196,21 @@ public function testHttpClientRequestHttpError(): void
self::assertEquals($exception->getCode(), HttpResponseStatusEnum::STATUS_404->getCode());
}
}

public function testHttpClientNetworkHttpError(): void
{
$requestMatcher = new RequestMatcher();
$builtRequest = null;
$this->mockHttpClient->on($requestMatcher, function (RequestInterface $request) use (&$builtRequest) {
$builtRequest = $request;

throw new NetworkException('NetworkException', $request);
});

try {
$this->client->request(new Query());
} catch (HttpNetworkException $exception) {
self::assertSame($builtRequest, $exception->getRequest());
}
}
}

0 comments on commit 1556139

Please sign in to comment.