Skip to content

Commit

Permalink
PSR-17 and PSR-18 and response logging
Browse files Browse the repository at this point in the history
  • Loading branch information
htollefsen committed Jun 19, 2019
1 parent eeb33aa commit 34b2358
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 54 deletions.
11 changes: 6 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,24 @@
"php": ">=7.1",
"ext-json": "*",
"bokbasen/php-sdk-auth": "^2",
"psr/http-message": "^1.0",
"php-http/client-implementation": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"php-http/httplug": "^2.0",
"php-http/message-factory": "^1.0",
"php-http/discovery": "^1.0"
},
"require-dev": {
"guzzlehttp/psr7": "^1.0",
"monolog/monolog": "^1.23",
"php-http/message": "^1.0",
"php-http/mock-client": "^1.0",
"php-http/message": "^1.0",
"nyholm/psr7": "^1.1",
"guzzlehttp/psr7": "^1.0",
"php-http/guzzle6-adapter": "^2.0",
"phpunit/phpunit": "^6"
},
"config": {
"sort-packages": true,
"branch-alias": {
"dev-master": "2.x-dev"
"dev-master": "3.x-dev"
}
}
}
3 changes: 3 additions & 0 deletions scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ build:
php72:
environment:
php: 7.2
php73:
environment:
php: 7.3

cache:
directories:
Expand Down
69 changes: 40 additions & 29 deletions src/ApiClient/Caller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,48 @@
namespace Bokbasen\ApiClient;

use Bokbasen\ApiClient\Exceptions\BokbasenApiClientException;
use Http\Client\HttpClient;
use Http\Discovery\HttpClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Message\MessageFactory;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

class Caller
{
/**
* @var HttpClient
* @var ClientInterface
*/
private $httpClient;

/**
* @var MessageFactory
* @var RequestFactoryInterface
*/
private $messageFactory;
private $requestFactory;

/**
* @var StreamFactoryInterface
*/
private $streamFactory;

public function __construct(
ClientInterface $httpClient = null,
RequestFactoryInterface $requestFactory = null,
StreamFactoryInterface $streamFactory = null
) {
$this->httpClient = $httpClient ?: Psr18ClientDiscovery::find();
$this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory();
$this->streamFactory = $streamFactory ?: Psr17FactoryDiscovery::findStreamFactory();
}

/**
* @param string $method
* @param string|UriInterface $url
* @param array $headers
* @param resource|string|StreamInterface|null $body
* @param string|StreamInterface|null $body
*
* @return ResponseInterface
*
Expand All @@ -36,33 +53,27 @@ class Caller
public function request(string $method, $url, array $headers = [], $body = null): ResponseInterface
{
try {
return $this->getHttpClient()->sendRequest(
$this->getMessageFactory()->createRequest($method, $url, $headers, $body)
);
} catch (\Http\Client\Exception | \Exception $e) {
throw new BokbasenApiClientException($e->getMessage(), $e->getCode(), $e);
}
}
$request = $this->requestFactory->createRequest($method, $url);

protected function getHttpClient(): HttpClient
{
if (!$this->httpClient) {
$this->httpClient = HttpClientDiscovery::find();
}
if (!empty($headers)) {
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
}

return $this->httpClient;
}
if ($body !== null) {
$request = $request->withBody(
$this->streamFactory->createStream($body)
);
}

protected function getMessageFactory(): MessageFactory
{
if (!$this->messageFactory) {
$this->messageFactory = MessageFactoryDiscovery::find();
return $this->httpClient->sendRequest($request);
} catch (ClientExceptionInterface | \Exception $e) {
throw new BokbasenApiClientException($e->getMessage(), $e->getCode(), $e);
}

return $this->messageFactory;
}

public function setHttpClient(HttpClient $httpClient): void
public function setHttpClient(ClientInterface $httpClient): void
{
$this->httpClient = $httpClient;
}
Expand Down
54 changes: 43 additions & 11 deletions src/ApiClient/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,25 @@ public function setHttpClient(HttpClient $httpClient): void
/**
* @throws BokbasenApiClientException
*/
protected function call(string $method, string $path, array $headers = [], $body = null, bool $authenticate = true): ResponseInterface
protected function call(string $method, string $path, array $headers = [], ?string $body = null, bool $authenticate = true): ResponseInterface
{
$headers = $authenticate ? $this->addAuthenticationHeaders($headers) : $headers;
$url = $this->prependBaseUrl($path);

$this->logRequest($method, $url, $body);

return $this->getCaller()->request($method, $url, $headers, $body);
$response = $this->getCaller()->request($method, $url, $headers, $body);

$this->logResponse($response);

return $response;
}

/**
* Execute POST request
*
* @param string $path
* @param resource|string|StreamInterface|null $body
* @param string|StreamInterface|null $body
* @param array $headers
* @param bool $authenticate
*
Expand All @@ -115,7 +119,7 @@ public function post(string $path, $body, array $headers = [], bool $authenticat
* Execute PUT request
*
* @param string $path
* @param resource|string|StreamInterface|null $body
* @param string|StreamInterface|null $body
* @param array $headers
* @param bool $authenticate
*
Expand All @@ -138,7 +142,7 @@ public function put(string $path, $body, array $headers = [], bool $authenticate
* Execute GET request
*
* @param string $path
* @param resource|string|StreamInterface|null $body
* @param string|StreamInterface|null $body
* @param array $headers
* @param bool $authenticate
*
Expand All @@ -161,7 +165,7 @@ public function get(string $path, array $headers = [], $authenticate = true): Re
* Execute PATCH request
*
* @param string $path
* @param resource|string|StreamInterface|null $body
* @param string|StreamInterface|null $body
* @param array $headers
* @param bool $authenticate
*
Expand Down Expand Up @@ -225,14 +229,42 @@ protected function addAuthenticationHeaders(array $existingHeaders = []): array
return array_merge($this->login->getAuthHeadersAsArray(), $existingHeaders);
}

protected function logRequest(string $method, string $url, $body = null): void
protected function logRequest(string $method, string $url, ?string $body = null): void
{
if ($this->logger) {
$message = sprintf('Executing HTTP %s request to %s', $method, $url);
$logItem = [
'method' => $method,
'url' => $url,
];

if (!empty($body)) {
$message .= sprintf(' with data %s', $body);
$logItem['body'] = (string) $body;
}
$this->logger->debug($message);
$this->logger->info(json_encode($logItem));
}
}

protected function logResponse(ResponseInterface $response): void
{
if ($this->logger) {
$logItem = [
'code' => $response->getStatusCode(),
'headers' => $response->getHeaders(),
];

try {
$body = $response->getBody()->getContents();
} catch (\RuntimeException $e) {
$this->logger->warning('Unable to extract body in logger');
}

if (!empty($body)) {
$logItem['body'] = $body;
}

$this->logger->info(json_encode($logItem));

$response->getBody()->rewind();
}
}
}
}
21 changes: 21 additions & 0 deletions tests/ApiClient/CallerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Tests\Bokbasen\ApiClient;

use Bokbasen\ApiClient\Caller;
use Http\Mock\Client;
use PHPUnit\Framework\TestCase;

class CallerTest extends TestCase
{
public function testRequest()
{
$caller = new Caller(new Client());
$response = $caller->request('GET', 'https://example.com');

$this->assertEquals(
200,
$response->getStatusCode()
);
}
}
24 changes: 15 additions & 9 deletions tests/ApiClient/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Bokbasen\Auth\Login;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use Nyholm\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Http\Mock\Client as HttpClient;
Expand All @@ -21,7 +22,7 @@ private function getLogin()
$stub->method('getAuthHeadersAsArray')
->willReturn([
'Authorization' => 'Boknett TGT-123123-123123-123123',
'Date' => gmdate(Login::HTTP_HEADER_DATE_FORMAT)
'Date' => gmdate(Login::HTTP_HEADER_DATE_FORMAT),
]);

return $stub;
Expand All @@ -32,6 +33,7 @@ private function getClient($url = 'http://client.test')
$client = new Client($this->getLogin(), $url);

$httpClient = new HttpClient();
$httpClient->addResponse(new Response(200, [], '{"body": "example"}'));
$client->setHttpClient($httpClient);

return $client;
Expand All @@ -45,11 +47,15 @@ public function testSetLogger()
$client->setLogger($logger);

$client->post('/path', json_encode(["data" => "data"]));
list($record) = $handler->getRecords();
list($request, $response) = $handler->getRecords();

$this->assertEquals(
'Executing HTTP POST request to http://client.test/path with data {"data":"data"}',
$record['message']
'{"method":"POST","url":"http:\/\/client.test\/path","body":"{\"data\":\"data\"}"}',
$request['message']
);
$this->assertEquals(
'{"code":200,"headers":[]}',
$response['message']
);
}

Expand All @@ -60,7 +66,7 @@ public function testGet()
$response = $client->get('/getpath');
$this->assertInstanceOf(ResponseInterface::class, $response);

$response = $client->get('/getpath', ["Content-Type" => "application/json"],false);
$response = $client->get('/getpath', ["Content-Type" => "application/json"], false);
$this->assertInstanceOf(ResponseInterface::class, $response);
}

Expand All @@ -71,7 +77,7 @@ public function testPost()
$response = $client->post('/path', json_encode(['my' => 'body']));
$this->assertInstanceOf(ResponseInterface::class, $response);

$response = $client->post('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"],false);
$response = $client->post('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"], false);
$this->assertInstanceOf(ResponseInterface::class, $response);
}

Expand All @@ -82,7 +88,7 @@ public function testPut()
$response = $client->put('/path', json_encode(['my' => 'body']));
$this->assertInstanceOf(ResponseInterface::class, $response);

$response = $client->put('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"],false);
$response = $client->put('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"], false);
$this->assertInstanceOf(ResponseInterface::class, $response);
}

Expand All @@ -93,7 +99,7 @@ public function testPatch()
$response = $client->patch('/path', json_encode(['my' => 'body']));
$this->assertInstanceOf(ResponseInterface::class, $response);

$response = $client->patch('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"],false);
$response = $client->patch('/path', json_encode(['my' => 'body']), ["Content-Type" => "application/json"], false);
$this->assertInstanceOf(ResponseInterface::class, $response);
}

Expand All @@ -104,7 +110,7 @@ public function testPostJson()
$response = $client->postJson('/path', ['my' => 'body']);
$this->assertInstanceOf(ResponseInterface::class, $response);

$response = $client->postJson('/path', ['my' => 'body'], ["Content-Type" => "application/json"],false);
$response = $client->postJson('/path', ['my' => 'body'], ["Content-Type" => "application/json"], false);
$this->assertInstanceOf(ResponseInterface::class, $response);
}

Expand Down

0 comments on commit 34b2358

Please sign in to comment.