diff --git a/.travis.yml b/.travis.yml index 6038996..fbad42e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,10 @@ addons: language: php php: - - 7.3 - - 7.2 - - 7.1 - - 7.0 - - 5.6 + - 7.4 before_script: - composer self-update - composer install -script: vendor/bin/phing test -Donly.units=true +script: vendor/bin/phpunit tests/ApiTest.php diff --git a/composer.json b/composer.json index 823c769..1b00e64 100644 --- a/composer.json +++ b/composer.json @@ -3,15 +3,18 @@ "description": "Wrapper for OVH APIs", "license": "BSD-3-Clause", "require": { - "guzzlehttp/guzzle": "^6.0" + "php": ">=7.4", + "guzzlehttp/guzzle": "^6.0||^7.0", + "ext-json": "*" }, "autoload": { "psr-4": {"Ovh\\": "src/"} }, "require-dev": { - "phpunit/phpunit": "4.*", - "phpdocumentor/phpdocumentor": "2.*", - "squizlabs/php_codesniffer": "2.*", - "phing/phing": "^2.14" + "phpunit/phpunit": "^9.5", + "phpdocumentor/phpdocumentor": "v3.0.0", + "squizlabs/php_codesniffer": "^3.5", + "phpdocumentor/graphviz": "^2.0@dev", + "symfony/flex": "^1.11" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index aa6b117..0f9e1cf 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,19 +1,19 @@ - - - - tests - - - - - - - - - - - src - - + + + + src + + + + + tests + + + + + + + + diff --git a/src/Api.php b/src/Api.php index 77b189e..0286b98 100644 --- a/src/Api.php +++ b/src/Api.php @@ -31,8 +31,11 @@ namespace Ovh; use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use Psr\Http\Message\ResponseInterface; /** * Wrapper to manage login and exchanges with simpliest Ovh API @@ -68,42 +71,42 @@ class Api * * @var string */ - private $endpoint = null; + private ?string $endpoint; /** * Contain key of the current application * * @var string */ - private $application_key = null; + private ?string $application_key; /** * Contain secret of the current application * * @var string */ - private $application_secret = null; + private ?string $application_secret; /** * Contain consumer key of the current application * * @var string */ - private $consumer_key = null; + private ?string $consumer_key; /** * Contain delta between local timestamp and api server timestamp * * @var string */ - private $time_delta = null; + private ?string $time_delta; /** * Contain http client connection * * @var Client */ - private $http_client = null; + private ?Client $http_client; /** * Construct a new wrapper instance @@ -130,19 +133,14 @@ class Api throw new Exceptions\InvalidParameterException("Endpoint parameter is empty"); } - if (preg_match('/^https?:\/\/..*/',$api_endpoint)) - { - $this->endpoint = $api_endpoint; - } - else - { - if (!array_key_exists($api_endpoint, $this->endpoints)) { - throw new Exceptions\InvalidParameterException("Unknown provided endpoint"); - } - else - { + if (preg_match('/^https?:\/\/..*/', $api_endpoint)) { + $this->endpoint = $api_endpoint; + } else { + if (!array_key_exists($api_endpoint, $this->endpoints)) { + throw new Exceptions\InvalidParameterException("Unknown provided endpoint"); + } + $this->endpoint = $this->endpoints[$api_endpoint]; - } } if (!isset($http_client)) { @@ -156,13 +154,12 @@ class Api $this->application_secret = $application_secret; $this->http_client = $http_client; $this->consumer_key = $consumer_key; - $this->time_delta = null; } /** * Calculate time delta between local machine and API's server * - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error * @return int */ private function calculateTimeDelta() @@ -189,7 +186,7 @@ class Api * @param string $redirection url to redirect on your website after authentication * * @return mixed - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error */ public function requestCredentials( array $accessRules, @@ -218,18 +215,20 @@ class Api * This is the main method of this wrapper. It will * sign a given query and return its result. * - * @param string $method HTTP method of request (GET,POST,PUT,DELETE) - * @param string $path relative url of API request - * @param \stdClass|array|null $content body of the request - * @param bool $is_authenticated if the request use authentication + * @param string $method HTTP method of request (GET,POST,PUT,DELETE) + * @param string $path relative url of API request + * @param \stdClass|array|null $content body of the request + * @param bool $is_authenticated if the request use authentication * - * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @param null $headers + * @return ResponseInterface + * @throws Exceptions\InvalidParameterException + * @throws GuzzleException + * @throws \JsonException */ - protected function rawCall($method, $path, $content = null, $is_authenticated = true, $headers = null) + protected function rawCall($method, $path, $content = null, $is_authenticated = true, $headers = null): ResponseInterface { - if ( $is_authenticated ) - { + if ($is_authenticated) { if (!isset($this->application_key)) { throw new Exceptions\InvalidParameterException("Application key parameter is empty"); } @@ -241,7 +240,7 @@ class Api $url = $this->endpoint . $path; $request = new Request($method, $url); - if (isset($content) && $method == 'GET') { + if (isset($content) && $method === 'GET') { $query_string = $request->getUri()->getQuery(); $query = array(); @@ -270,20 +269,18 @@ class Api $request = $request->withUri($url); $body = ""; } elseif (isset($content)) { - $body = json_encode($content, JSON_UNESCAPED_SLASHES); + $body = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES); $request->getBody()->write($body); } else { $body = ""; } - if(!is_array($headers)) - { + if (!is_array($headers)) { $headers = []; } $headers['Content-Type'] = 'application/json; charset=utf-8'; if ($is_authenticated) { - $headers['X-Ovh-Application'] = $this->application_key; if (!isset($this->time_delta)) { @@ -309,41 +306,38 @@ class Api /** * Decode a Response object body to an Array * - * @param Response $response + * @param Response $response * - * @return array + * @throws \JsonException */ private function decodeResponse(Response $response) { - return json_decode($response->getBody(), true); + return json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR); } /** * Wrap call to Ovh APIs for GET requests * - * @param string $path path ask inside api - * @param array $content content to send inside body of request + * @param string $path path ask inside api + * @param array $content content to send inside body of request * @param array headers custom HTTP headers to add on the request * @param bool is_authenticated if the request need to be authenticated * - * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error + * @throws \JsonException */ public function get($path, $content = null, $headers = null, $is_authenticated = true) { - if(preg_match('/^\/[^\/]+\.json$/', $path)) - { - // Schema description must be access without authentication - return $this->decodeResponse( - $this->rawCall("GET", $path, $content, false, $headers) - ); - } - else - { - return $this->decodeResponse( - $this->rawCall("GET", $path, $content, $is_authenticated, $headers) - ); + if (preg_match('/^\/[^\/]+\.json$/', $path)) { + // Schema description must be access without authentication + return $this->decodeResponse( + $this->rawCall("GET", $path, $content, false, $headers) + ); } + + return $this->decodeResponse( + $this->rawCall("GET", $path, $content, $is_authenticated, $headers) + ); } /** @@ -354,8 +348,7 @@ class Api * @param array headers custom HTTP headers to add on the request * @param bool is_authenticated if the request need to be authenticated * - * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error */ public function post($path, $content = null, $headers = null, $is_authenticated = true) { @@ -372,8 +365,7 @@ class Api * @param array headers custom HTTP headers to add on the request * @param bool is_authenticated if the request need to be authenticated * - * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error */ public function put($path, $content, $headers = null, $is_authenticated = true) { @@ -390,8 +382,7 @@ class Api * @param array headers custom HTTP headers to add on the request * @param bool is_authenticated if the request need to be authenticated * - * @return array - * @throws \GuzzleHttp\Exception\ClientException if http request is an error + * @throws ClientException if http request is an error */ public function delete($path, $content = null, $headers = null, $is_authenticated = true) { @@ -403,7 +394,7 @@ class Api /** * Get the current consumer key */ - public function getConsumerKey() + public function getConsumerKey(): ?string { return $this->consumer_key; } @@ -411,7 +402,7 @@ class Api /** * Return instance of http client */ - public function getHttpClient() + public function getHttpClient(): ?Client { return $this->http_client; } diff --git a/src/Exceptions/ApiException.php b/src/Exceptions/ApiException.php index c2b866a..4c8d2e2 100644 --- a/src/Exceptions/ApiException.php +++ b/src/Exceptions/ApiException.php @@ -43,5 +43,4 @@ use Exception; */ class ApiException extends Exception { - } diff --git a/src/Exceptions/InvalidParameterException.php b/src/Exceptions/InvalidParameterException.php index 78902f9..6df582b 100644 --- a/src/Exceptions/InvalidParameterException.php +++ b/src/Exceptions/InvalidParameterException.php @@ -43,5 +43,4 @@ use Exception; */ class InvalidParameterException extends Exception { - } diff --git a/src/Exceptions/NotLoggedException.php b/src/Exceptions/NotLoggedException.php index 23b2f6d..6384fc5 100644 --- a/src/Exceptions/NotLoggedException.php +++ b/src/Exceptions/NotLoggedException.php @@ -42,5 +42,4 @@ use Exception; */ class NotLoggedException extends Exception { - } diff --git a/tests/ApiFunctionalTest.php b/tests/ApiFunctionalTest.php index 59d3954..512509c 100644 --- a/tests/ApiFunctionalTest.php +++ b/tests/ApiFunctionalTest.php @@ -28,7 +28,9 @@ namespace Ovh\tests; use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; use Ovh\Api; +use PHPUnit\Framework\TestCase; /** * Functional tests of Api class @@ -36,7 +38,7 @@ use Ovh\Api; * @package Ovh * @category Ovh */ -class ApiFunctionalTest extends \PHPUnit_Framework_TestCase +class ApiFunctionalTest extends TestCase { /** @@ -82,7 +84,7 @@ class ApiFunctionalTest extends \PHPUnit_Framework_TestCase /** * Define id to create object */ - protected function setUp() + protected function setUp() :void { $this->application_key = getenv('APP_KEY'); $this->application_secret = getenv('APP_SECRET'); @@ -238,7 +240,7 @@ class ApiFunctionalTest extends \PHPUnit_Framework_TestCase */ public function testApiGetWithParameters() { - $this->setExpectedException('\\GuzzleHttp\\Exception\\ClientException', '400'); + $this->expectException(ClientException::class); $this->api->get('/me/accessRestriction/ip', ['foo' => 'bar']); } diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 970fb6d..fbd6aea 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -28,10 +28,14 @@ namespace Ovh\tests; use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Middleware; +use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Request; use Ovh\Api; +use Ovh\Exceptions\InvalidParameterException; +use PHPUnit\Framework\TestCase; /** * Test Api class @@ -39,7 +43,7 @@ use Ovh\Api; * @package Ovh * @category Ovh */ -class ApiTest extends \PHPUnit_Framework_TestCase +class ApiTest extends TestCase { /** * @var Client @@ -69,7 +73,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase /** * Define id to create object */ - protected function setUp() + protected function setUp() :void { $this->application_key = 'app_key'; $this->application_secret = 'app_secret'; @@ -116,7 +120,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testMissingApplicationKey() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Application key'); + $this->expectException(InvalidParameterException::class); $api = new Api(null, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $api->get('/me'); } @@ -126,7 +130,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testMissingApplicationSecret() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Application secret'); + $this->expectException(InvalidParameterException::class); $api = new Api($this->application_key, null, $this->endpoint, $this->consumer_key, $this->client); $api->get('/me'); } @@ -142,14 +146,19 @@ class ApiTest extends \PHPUnit_Framework_TestCase return $request; } - $request = $request->withUri($request->getUri() - ->withHost('httpbin.org') - ->withPath('/') - ->withQuery('')); - return $request; + return null; + })); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('{}'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); })); $api = new Api(NULL, NULL, $this->endpoint, $this->consumer_key, $this->client); - $api->get('/1.0/unauthcall', null, null, false); + $api->get('/unauthcall', null, null, false); + $this->assertEquals(1, 1); } /** @@ -157,7 +166,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testMissingApiEndpoint() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Endpoint'); + $this->expectException(InvalidParameterException::class); new Api($this->application_key, $this->application_secret, null, $this->consumer_key, $this->client); } @@ -166,7 +175,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testBadApiEndpoint() { - $this->setExpectedException('\\Ovh\\Exceptions\\InvalidParameterException', 'Unknown'); + $this->expectException(InvalidParameterException::class); new Api($this->application_key, $this->application_secret, 'i_am_invalid', $this->consumer_key, $this->client); } @@ -190,11 +199,11 @@ class ApiTest extends \PHPUnit_Framework_TestCase $handlerStack = $this->client->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { - $body = $response->getBody(); - $body->write(time() - 10); + $body = Psr7\Utils::streamFor(time() - 10); return $response ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') ->withBody($body); })); @@ -217,16 +226,19 @@ class ApiTest extends \PHPUnit_Framework_TestCase $handlerStack = $this->client->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { - $body = $response->getBody(); - $body->write('{"validationUrl":"https://api.ovh.com/login/?credentialToken=token","consumerKey":"consumer_remote","state":"pendingValidation"}'); + $body = Psr7\Utils::streamFor('{"validationUrl":"https://api.ovh.com/login/?credentialToken=token","consumerKey":"consumer_remote","state":"pendingValidation"}'); return $response ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') ->withBody($body); })); $property = self::getPrivateProperty('consumer_key'); + $this->assertEquals('consumer', $this->consumer_key); + $this->assertNotEquals('consumer_remote', $this->consumer_key); + $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $accessRules = [json_decode(' { "method": "GET", "path": "/*" } ')]; @@ -235,6 +247,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase $consumer_key = $property->getValue($api); $this->assertEquals($consumer_key, $credentials["consumerKey"]); + $this->assertEquals('consumer_remote', $credentials["consumerKey"]); $this->assertNotEquals($consumer_key, $this->consumer_key); } @@ -243,15 +256,13 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testInvalidApplicationKey() { - $this->setExpectedException( - '\GuzzleHttp\Exception\ClientException' - ); + + $this->expectException(ClientException::class); $handlerStack = $this->client->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { - $body = $response->getBody(); - $body->write('{\"message\":\"Invalid application key\"}'); + $body = Psr7\Utils::streamFor('{\"message\":\"Invalid application key\"}'); return $response ->withStatus(401, 'POUET') @@ -276,15 +287,12 @@ class ApiTest extends \PHPUnit_Framework_TestCase */ public function testInvalidRight() { - $this->setExpectedException( - '\GuzzleHttp\Exception\ClientException' - ); + $this->expectException(ClientException::class); $handlerStack = $this->client->getConfig('handler'); $handlerStack->push(Middleware::mapResponse(function (Response $response) { - $body = $response->getBody(); - $body->write('{\"message\":\"Invalid credentials\"}'); + $body = Psr7\Utils::streamFor('{\"message\":\"Invalid credentials\"}'); return $response ->withStatus(403) @@ -326,9 +334,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $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']); @@ -354,9 +367,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $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"]); @@ -382,9 +400,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $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]); @@ -416,9 +439,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $api = new Api($this->application_key, $this->application_secret, 'ovh-ca', $this->consumer_key, $this->client); $api->get('/me/api/credential'); @@ -450,9 +478,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $api = new Api($this->application_key, $this->application_secret, 'http://api.ovh.com/1.0', $this->consumer_key, $this->client); $api->get('/me/api/credential'); @@ -484,9 +517,14 @@ class ApiTest extends \PHPUnit_Framework_TestCase ->withQuery('')); return $request; })); - //$handlerStack->push(Middleware::mapResponse(function (Response $response) { - // return $response; - //})); + $handlerStack->push(Middleware::mapResponse(function (Response $response) { + $body = Psr7\Utils::streamFor('123456789991'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); + })); $api = new Api($this->application_key, $this->application_secret, 'https://api.ovh.com/1.0', $this->consumer_key, $this->client); $api->get('/me/api/credential'); @@ -514,10 +552,16 @@ class ApiTest extends \PHPUnit_Framework_TestCase return $request; })); $handlerStack->push(Middleware::mapResponse(function (Response $response) { - return $response->withStatus(200); + $body = Psr7\Utils::streamFor('{"validationUrl":"https://api.ovh.com/login/?credentialToken=token","consumerKey":"consumer_remote","state":"pendingValidation"}'); + + return $response + ->withStatus(200) + ->withHeader('Content-Type', 'application/json; charset=utf-8') + ->withBody($body); })); $api = new Api($this->application_key, $this->application_secret, $this->endpoint, $this->consumer_key, $this->client); $api->requestCredentials([]); } + }