diff --git a/composer.json b/composer.json index 94e672e..991a5ed 100644 --- a/composer.json +++ b/composer.json @@ -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" } } } diff --git a/scrutinizer.yml b/scrutinizer.yml index 847802d..523d2df 100644 --- a/scrutinizer.yml +++ b/scrutinizer.yml @@ -29,6 +29,9 @@ build: php72: environment: php: 7.2 + php73: + environment: + php: 7.3 cache: directories: diff --git a/src/ApiClient/Caller.php b/src/ApiClient/Caller.php index 1d1ef30..2982079 100644 --- a/src/ApiClient/Caller.php +++ b/src/ApiClient/Caller.php @@ -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 * @@ -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; } diff --git a/src/ApiClient/Client.php b/src/ApiClient/Client.php index 52238dc..aeab845 100644 --- a/src/ApiClient/Client.php +++ b/src/ApiClient/Client.php @@ -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 * @@ -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 * @@ -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 * @@ -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 * @@ -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(); } } -} \ No newline at end of file +} diff --git a/tests/ApiClient/CallerTest.php b/tests/ApiClient/CallerTest.php new file mode 100644 index 0000000..a3174f5 --- /dev/null +++ b/tests/ApiClient/CallerTest.php @@ -0,0 +1,21 @@ +request('GET', 'https://example.com'); + + $this->assertEquals( + 200, + $response->getStatusCode() + ); + } +} diff --git a/tests/ApiClient/ClientTest.php b/tests/ApiClient/ClientTest.php index 44f96c0..f317040 100644 --- a/tests/ApiClient/ClientTest.php +++ b/tests/ApiClient/ClientTest.php @@ -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; @@ -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; @@ -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; @@ -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'] ); } @@ -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); } @@ -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); } @@ -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); } @@ -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); } @@ -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); }