From 55299060c950d9cd04ad5f3fa9b129240ce68ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 18:54:01 +0200 Subject: [PATCH 01/11] Update composer to use Httplug adapters `php-http/guzzle-adapter` is still required as dev dependency because it is used as the base adapter. All the unit tests required that client to work. --- composer.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 823c769..15f262f 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,10 @@ "description": "Wrapper for OVH APIs", "license": "BSD-3-Clause", "require": { - "guzzlehttp/guzzle": "^6.0" + "guzzlehttp/psr7": "~1.2", + "php-http/httplug": "^1.0", + "php-http/client-implementation": "^1.0", + "php-http/discovery": "^0.8" }, "autoload": { "psr-4": {"Ovh\\": "src/"} @@ -12,6 +15,7 @@ "phpunit/phpunit": "4.*", "phpdocumentor/phpdocumentor": "2.*", "squizlabs/php_codesniffer": "2.*", - "phing/phing": "^2.14" + "phing/phing": "^2.14", + "php-http/guzzle6-adapter": "^1.0" } } From d2f1c0dab1afae30059b7802d3467a1c5ac6eb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 18:58:47 +0200 Subject: [PATCH 02/11] Update bindings to use the `Http\Client\*` objects --- src/Api.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Api.php b/src/Api.php index dd7d10c..ec64962 100644 --- a/src/Api.php +++ b/src/Api.php @@ -30,9 +30,11 @@ namespace Ovh; -use GuzzleHttp\Client; +use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use Http\Client\HttpClient; +use Http\Message\MessageFactory; /** * Wrapper to manage login and exchanges with simpliest Ovh API @@ -100,7 +102,7 @@ class Api /** * Contain http client connection * - * @var Client + * @var HttpClient */ private $http_client = null; @@ -114,7 +116,7 @@ class Api * @param string $api_endpoint name of api selected * @param string $consumer_key If you have already a consumer key, this parameter prevent to do a * new authentication - * @param Client $http_client instance of http client + * @param HttpClient $http_client instance of http client * * @throws Exceptions\InvalidParameterException if one parameter is missing or with bad value */ @@ -123,7 +125,7 @@ public function __construct( $application_secret, $api_endpoint, $consumer_key = null, - Client $http_client = null + HttpClient $http_client = null ) { if (!isset($application_key)) { throw new Exceptions\InvalidParameterException("Application key parameter is empty"); @@ -159,7 +161,7 @@ public function __construct( /** * Calculate time delta between local machine and API's server * - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error * @return int */ private function calculateTimeDelta() @@ -186,7 +188,7 @@ private function calculateTimeDelta() * @param string $redirection url to redirect on your website after authentication * * @return mixed - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ public function requestCredentials( array $accessRules, @@ -221,7 +223,7 @@ public function requestCredentials( * @param bool $is_authenticated if the request use authentication * * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ private function rawCall($method, $path, $content = null, $is_authenticated = true, $headers = null) { @@ -309,7 +311,7 @@ private function decodeResponse(Response $response) * @param array $content content to send inside body of request * * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ public function get($path, $content = null, $headers = null) { @@ -325,7 +327,7 @@ public function get($path, $content = null, $headers = null) * @param array $content content to send inside body of request * * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ public function post($path, $content = null, $headers = null) { @@ -341,7 +343,7 @@ public function post($path, $content = null, $headers = null) * @param array $content content to send inside body of request * * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ public function put($path, $content, $headers = null) { @@ -357,7 +359,7 @@ public function put($path, $content, $headers = null) * @param array $content content to send inside body of request * * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws \Http\Exception\TransferException if http request is an error */ public function delete($path, $content = null, $headers = null) { From 3203897e05b98aa1197263492c2931134738223c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 18:59:08 +0200 Subject: [PATCH 03/11] Remove default client creation logic --- src/Api.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Api.php b/src/Api.php index ec64962..fa5c8f5 100644 --- a/src/Api.php +++ b/src/Api.php @@ -143,13 +143,6 @@ public function __construct( throw new Exceptions\InvalidParameterException("Unknown provided endpoint"); } - if (!isset($http_client)) { - $http_client = new Client([ - 'timeout' => 30, - 'connect_timeout' => 5, - ]); - } - $this->application_key = $application_key; $this->endpoint = $this->endpoints[$api_endpoint]; $this->application_secret = $application_secret; From 3cfd9e4b3040ae483b5131738d274ee0589d2331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:00:42 +0200 Subject: [PATCH 04/11] Update `rawCall` request creation logic All the data are computed before `Request` creation. It allow to perform a simple `new Request` with all the analysis result. Headers are set with authentication, URI is cleaned... --- src/Api.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Api.php b/src/Api.php index fa5c8f5..8ac13db 100644 --- a/src/Api.php +++ b/src/Api.php @@ -221,9 +221,11 @@ public function requestCredentials( private function rawCall($method, $path, $content = null, $is_authenticated = true, $headers = null) { $url = $this->endpoint . $path; - $request = new Request($method, $url); + $uri = new Uri($url); + $body = null; + if (isset($content) && $method == 'GET') { - $query_string = $request->getUri()->getQuery(); + $query_string = $uri->getQuery(); $query = array(); if (!empty($query_string)) { @@ -246,16 +248,9 @@ private function rawCall($method, $path, $content = null, $is_authenticated = tr } $query = \GuzzleHttp\Psr7\build_query($query); - - $url = $request->getUri()->withQuery($query); - $request = $request->withUri($url); - $body = ""; + $uri = $uri->withQuery($query); } elseif (isset($content)) { $body = json_encode($content); - - $request->getBody()->write($body); - } else { - $body = ""; } if(!is_array($headers)) { @@ -281,8 +276,15 @@ private function rawCall($method, $path, $content = null, $is_authenticated = tr } } + $request = new Request( + $method, + $uri, + $headers, + $body + ); + /** @var Response $response */ - return $this->http_client->send($request, ['headers' => $headers]); + return $this->getHttpClient()->sendRequest($request); } /** From 9f6492185b2e14fe8d1e53f8a1d01715039f29a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:06:37 +0200 Subject: [PATCH 05/11] Create a central method to send the Request All the calls go through that method, it'll allow extensibility. --- src/Api.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Api.php b/src/Api.php index 8ac13db..111a06d 100644 --- a/src/Api.php +++ b/src/Api.php @@ -30,6 +30,8 @@ namespace Ovh; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; @@ -276,15 +278,13 @@ private function rawCall($method, $path, $content = null, $is_authenticated = tr } } - $request = new Request( + /** @var ResponseInterface $response */ + return $this->sendRequest(new Request( $method, $uri, $headers, $body - ); - - /** @var Response $response */ - return $this->getHttpClient()->sendRequest($request); + )); } /** @@ -378,4 +378,14 @@ public function getHttpClient() { return $this->http_client; } + + /** + * Send the Request through the HttpClient + * @param RequestInterface $request + * @return ResponseInterface + */ + private function sendRequest(RequestInterface $request) + { + return $this->getHttpClient()->sendRequest($request); + } } From ed48ac6ed1868d8f276b1ea0863bac9efa08f096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:06:49 +0200 Subject: [PATCH 06/11] Update PHPDoc --- src/Api.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Api.php b/src/Api.php index 111a06d..f2f7cc2 100644 --- a/src/Api.php +++ b/src/Api.php @@ -365,6 +365,7 @@ public function delete($path, $content = null, $headers = null) /** * Get the current consumer key + * @return string */ public function getConsumerKey() { @@ -373,6 +374,7 @@ public function getConsumerKey() /** * Return instance of http client + * @return HttpClient */ public function getHttpClient() { From 5fbcdf06b2cde21676c9dccd9d255f363f5d992f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:07:25 +0200 Subject: [PATCH 07/11] Cleanup comments --- tests/ApiTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 7c74d34..a834639 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -303,9 +303,6 @@ public function testGetQueryArgs() ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $api->get('/me/api/credential?applicationId=49', ['status' => 'pendingValidation']); @@ -331,9 +328,6 @@ public function testGetOverlappingQueryArgs() ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $api->get('/me/api/credential?applicationId=49&status=pendingValidation', ['status' => 'expired', 'test' => "success"]); @@ -359,9 +353,6 @@ public function testGetBooleanQueryArgs() ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $api->get('/me/api/credential', ['dryRun' => true, 'notDryRun' => false]); From 94de85dfaf6b667dd2ff82b96972615ec7302d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:10:07 +0200 Subject: [PATCH 08/11] Update ApiTest to use a HttpClient instance Guzzle is still used inside the test logic with middlewares. --- tests/ApiTest.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index a834639..59ecbec 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -27,6 +27,7 @@ namespace Ovh\tests; +use Http\Adapter\Guzzle6\Client as HttpClient; use GuzzleHttp\Client; use GuzzleHttp\Middleware; use GuzzleHttp\Psr7\Response; @@ -76,7 +77,8 @@ protected function setUp() $this->consumer_key = 'consumer'; $this->endpoint = 'ovh-eu'; - $this->client = new Client(); + $this->guzzle = new Client(); + $this->client = new HttpClient($this->guzzle); } /** @@ -152,9 +154,8 @@ public function testBadApiEndpoint() */ public function testClientCreation() { - $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key); - - $this->assertInstanceOf('\\GuzzleHttp\\Client', $api->getHttpClient()); + $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); + $this->assertInstanceOf('Http\\Client\\HttpClient', $api->getHttpClient()); } /** @@ -164,7 +165,7 @@ public function testTimeDeltaCompute() { $delay = 10; - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { $body = $response->getBody(); @@ -191,7 +192,7 @@ public function testTimeDeltaCompute() */ public function testIfConsumerKeyIsReplace() { - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { $body = $response->getBody(); @@ -221,10 +222,10 @@ public function testIfConsumerKeyIsReplace() public function testInvalidApplicationKey() { $this->setExpectedException( - '\GuzzleHttp\Exception\ClientException' + 'Http\Client\Exception\HttpException' ); - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { $body = $response->getBody(); @@ -254,10 +255,10 @@ public function testInvalidApplicationKey() public function testInvalidRight() { $this->setExpectedException( - '\GuzzleHttp\Exception\ClientException' + 'Http\Client\Exception\HttpException' ); - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { $body = $response->getBody(); @@ -288,7 +289,7 @@ public function testGetConsumerKey() */ public function testGetQueryArgs() { - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapRequest(function (Request $request) { if($request->getUri()->getPath() == "/1.0/auth/time") { return $request; @@ -313,7 +314,7 @@ public function testGetQueryArgs() */ public function testGetOverlappingQueryArgs() { - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapRequest(function (Request $request) { if($request->getUri()->getPath() == "/1.0/auth/time") { return $request; @@ -338,7 +339,7 @@ public function testGetOverlappingQueryArgs() */ public function testGetBooleanQueryArgs() { - $handlerStack = $this->client->getConfig('handler'); + $handlerStack = $this->guzzle->getConfig('handler'); $handlerStack->push(Middleware::mapRequest(function (Request $request) { if($request->getUri()->getPath() == "/1.0/auth/time") { return $request; From c0d9223b15bdce1adbdbfbc5de58c01f9cd7fe05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:10:25 +0200 Subject: [PATCH 09/11] Update FQN reference, don't need to start with \ --- tests/ApiTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 59ecbec..a8bc585 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -118,7 +118,7 @@ protected static function getPrivateProperty($name) */ public function testMissingApplicationKey() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Application key'); + $this->setExpectedException('Ovh\Exceptions\InvalidParameterException', 'Application key'); new Api(null, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); } @@ -127,7 +127,7 @@ public function testMissingApplicationKey() */ public function testMissingApplicationSecret() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Application secret'); + $this->setExpectedException('Ovh\Exceptions\InvalidParameterException', 'Application secret'); new Api($this->application_key, null, $this->endpoint, $this->consumer_key, $this->client); } @@ -136,7 +136,7 @@ public function testMissingApplicationSecret() */ public function testMissingApiEndpoint() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Endpoint'); + $this->setExpectedException('Ovh\Exceptions\InvalidParameterException', 'Endpoint'); new Api($this->application_key, $this->application_secret, null, $this->consumer_key, $this->client); } @@ -145,7 +145,7 @@ public function testMissingApiEndpoint() */ public function testBadApiEndpoint() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Unknown'); + $this->setExpectedException('Ovh\Exceptions\InvalidParameterException', 'Unknown'); new Api($this->application_key, $this->application_secret, 'i_am_invalid', $this->consumer_key, $this->client); } From cf0ef79c42d5b113fb3f1cc499fbcca01fca35b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:11:00 +0200 Subject: [PATCH 10/11] Update ApiFunctionalTest to use HttpClient --- tests/ApiFunctionalTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ApiFunctionalTest.php b/tests/ApiFunctionalTest.php index 85a1f1b..0deb0c3 100644 --- a/tests/ApiFunctionalTest.php +++ b/tests/ApiFunctionalTest.php @@ -27,7 +27,7 @@ namespace Ovh\tests; -use GuzzleHttp\Client; +use Http\Adapter\Guzzle6\Client; use Ovh\Api; /** @@ -238,7 +238,7 @@ public function testIfRequestWithoutAuthenticationWorks() */ public function testApiGetWithParameters() { - $this->setExpectedException('\\GuzzleHttp\\Exception\\ClientException', '400'); + $this->setExpectedException('Http\Client\Exception\HttpException', '400'); $this->api->get('/me/accessRestriction/ip', ['foo' => 'bar']); } From 139b36db542e5747a8c48e7fb276f8e0cb4bdc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20HULARD?= Date: Mon, 4 Apr 2016 19:12:12 +0200 Subject: [PATCH 11/11] Add `HttpClientDiscovery` support Allow to perform a dynamic client resolution based on the current context. Require the puli composer extension to be registered : https://php-http.readthedocs.org/en/latest/discovery.html#installation --- src/Api.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Api.php b/src/Api.php index f2f7cc2..e068f2e 100644 --- a/src/Api.php +++ b/src/Api.php @@ -37,6 +37,7 @@ use GuzzleHttp\Psr7\Response; use Http\Client\HttpClient; use Http\Message\MessageFactory; +use Http\Discovery\HttpClientDiscovery; /** * Wrapper to manage login and exchanges with simpliest Ovh API @@ -254,8 +255,7 @@ private function rawCall($method, $path, $content = null, $is_authenticated = tr } elseif (isset($content)) { $body = json_encode($content); } - if(!is_array($headers)) - { + if (!is_array($headers)) { $headers = []; } $headers['Content-Type'] = 'application/json; charset=utf-8'; @@ -378,6 +378,10 @@ public function getConsumerKey() */ public function getHttpClient() { + if ($this->http_client === null) { + $this->http_client = HttpClientDiscovery::find(); + } + return $this->http_client; }