From 53517bed69741e7d137f4722dcaf5b2ef31417eb Mon Sep 17 00:00:00 2001 From: Bart van Amelsvoort Date: Fri, 1 Jul 2016 15:48:57 +0200 Subject: [PATCH 1/4] Support Guzzle 6.x --- README.md | 3 +-- composer.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c2b0234..be35fcf 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,7 @@ by [Freshheads](https://www.freshheads.com) and will be maintained in sync with Requirements ------------ -FHPostcodeAPIClient works with PHP 5.4.0 or up. This library is dependent on the awesome [Guzzle](http://guzzlephp.org/) HTTP client library. Guzzle 5 -version is used instead of the new Guzzle 6, as Guzzle 6 requires the php version to be higher than 5.5.0. +FHPostcodeAPIClient works with PHP 5.4.0 or up. This library is dependent on the awesome [Guzzle](http://guzzlephp.org/) HTTP client library. Installation ------------ diff --git a/composer.json b/composer.json index 2b757d3..96ff740 100755 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=5.4.0", - "guzzlehttp/guzzle": "^5.3" + "guzzlehttp/guzzle": "^5.2|^6.0" }, "autoload": { "psr-0": { "FH\\PostcodeAPI": "lib/" } From 51062a66207b16bff003d63de2021eb7c2815377 Mon Sep 17 00:00:00 2001 From: Bart van Amelsvoort Date: Sat, 2 Jul 2016 16:39:06 +0200 Subject: [PATCH 2/4] guzzle 6 --- composer.json | 4 +- lib/FH/PostcodeAPI/Client.php | 40 +++++-------------- .../CouldNotParseResponseException.php | 2 +- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/composer.json b/composer.json index 96ff740..66d5996 100755 --- a/composer.json +++ b/composer.json @@ -17,14 +17,14 @@ ], "require": { "php": ">=5.4.0", - "guzzlehttp/guzzle": "^5.2|^6.0" + "guzzlehttp/guzzle": "^6.0" }, "autoload": { "psr-0": { "FH\\PostcodeAPI": "lib/" } }, "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0.x-dev" } } } diff --git a/lib/FH/PostcodeAPI/Client.php b/lib/FH/PostcodeAPI/Client.php index 2a43bb0..a8e8823 100755 --- a/lib/FH/PostcodeAPI/Client.php +++ b/lib/FH/PostcodeAPI/Client.php @@ -6,8 +6,8 @@ use GuzzleHttp\Client as HTTPClient; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Psr7\Request; +use Psr\Http\Message\ResponseInterface; /** * Client library for postcodeapi.nu 2.0 web service. @@ -16,7 +16,6 @@ */ class Client { - /** @var string */ const BASE_URI = 'https://postcode-api.apiwise.nl'; /** @@ -24,30 +23,9 @@ class Client */ private $httpClient; - /** - * @param ClientInterface $httpClient - * @param string $apiKey Required API key for authenticating client - */ - public function __construct(ClientInterface $httpClient, $apiKey) - { - $this->httpClient = $this->prepareClient($httpClient, $apiKey); - } - - /** - * @param ClientInterface $client - * @param string $apiKey - * - * @return HTTPClient - */ - private function prepareClient(ClientInterface $client, $apiKey) + public function __construct(ClientInterface $httpClient) { - if ($client->getDefaultOption('timeout') === null) { - $client->setDefaultOption('timeout', 5.0); - } - - $client->setDefaultOption('headers/X-Api-Key', $apiKey); - - return $client; + $this->httpClient = $httpClient; } /** @@ -107,7 +85,7 @@ private function parseResponse(ResponseInterface $response) $out = json_decode((string) $response->getBody()); if (json_last_error() !== JSON_ERROR_NONE) { - throw new CouldNotParseResponseException('Could not parse resonse', $response); + throw new CouldNotParseResponseException('Could not parse repsonse', $response); } return $out; @@ -115,15 +93,15 @@ private function parseResponse(ResponseInterface $response) /** * @param string $method - * @param string $path + * @param string $url * @param array $queryParams * * @return Request */ - private function createHttpRequest($method, $path, array $queryParams = array()) + private function createHttpRequest($method, $url, array $queryParams = array()) { - $path = $path . (count($queryParams) > 0 ? '?' . http_build_query($queryParams) : ''); + $url = $url . (count($queryParams) > 0 ? '?' . http_build_query($queryParams) : ''); - return $this->httpClient->createRequest($method, $path); + return new Request($method, $url); } } diff --git a/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php b/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php index 0db3580..837544b 100644 --- a/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php +++ b/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php @@ -2,7 +2,7 @@ namespace FH\PostcodeAPI\Exception; -use GuzzleHttp\Message\ResponseInterface; +use Psr\Http\Message\ResponseInterface; /** * @author Gijs Nieuwenhuis From 6890c337c910e47327553b87d691e70d10a9b40c Mon Sep 17 00:00:00 2001 From: Evert Harmeling Date: Thu, 28 Jul 2016 14:41:56 +0200 Subject: [PATCH 3/4] Added /postcodes call --- .travis.yml | 1 - CHANGELOG.md | 4 ++ README.md | 3 ++ composer.json | 3 ++ lib/FH/PostcodeAPI/Client.php | 62 +++++++++++++++++++++----- tests/FH/PostcodeAPI/ClientTest.php | 69 +++++++++++++++-------------- 6 files changed, 96 insertions(+), 46 deletions(-) diff --git a/.travis.yml b/.travis.yml index e84276e..f96d9c7 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 5.4 - 5.5 - 5.6 - 7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index d921dff..6f23472 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.1.0 + +* Added `getPostcodes()` call, to get postcodes based on provided `latitude, longitude`. Note that this call is only available with a premium account. + ## 2.0.0 * Rewrite to support postcodeapi.nu version 2 diff --git a/README.md b/README.md index be35fcf..facfd4b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ $client = new \FH\PostcodeAPI\Client(new \GuzzleHttp\Client(), $apiKey); // call endpoints $response = $client->getAddresses('5041EB', 21); $response = $client->getAddress('0855200000061001'); + +// Note that this call is only available with a premium account +$response = $client->getPostcodes('51.566405', '5.077171'); ``` [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/freshheads/fhpostcodeapiclient/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/composer.json b/composer.json index 66d5996..a2149c8 100755 --- a/composer.json +++ b/composer.json @@ -19,6 +19,9 @@ "php": ">=5.4.0", "guzzlehttp/guzzle": "^6.0" }, + "require-dev": { + "phpunit/phpunit": "^4.3.5" + }, "autoload": { "psr-0": { "FH\\PostcodeAPI": "lib/" } }, diff --git a/lib/FH/PostcodeAPI/Client.php b/lib/FH/PostcodeAPI/Client.php index a8e8823..73eb29e 100755 --- a/lib/FH/PostcodeAPI/Client.php +++ b/lib/FH/PostcodeAPI/Client.php @@ -16,15 +16,35 @@ */ class Client { - const BASE_URI = 'https://postcode-api.apiwise.nl'; + const POSTCODES_SORT_DISTANCE = 'distance'; + + /** + * @var string + */ + private $uriScheme = 'https://'; + + /** + * @var null|string + */ + private $domain = 'postcode-api.apiwise.nl'; + + /** + * @var string + */ + private $version = 'v2'; /** * @var HTTPClient */ private $httpClient; - public function __construct(ClientInterface $httpClient) + + public function __construct(ClientInterface $httpClient, $domain = null) { + if (null !== $domain) { + $this->domain = $domain; + } + $this->httpClient = $httpClient; } @@ -33,11 +53,11 @@ public function __construct(ClientInterface $httpClient) * @param string|null $number * @param int $from * - * @return \StdClass + * @return \stdClass */ public function getAddresses($postcode = null, $number = null, $from = 0) { - return $this->get('/v2/addresses/', [ + return $this->get('/addresses/', [ 'postcode' => $postcode, 'number' => $number, 'from' => $from @@ -47,26 +67,44 @@ public function getAddresses($postcode = null, $number = null, $from = 0) /** * @param string $id * - * @return \StdClass + * @return \stdClass */ public function getAddress($id) { - return $this->get("/v2/addresses/{$id}"); + return $this->get(sprintf('/addresses/%s', $id)); + } + + /** + * @param string $latitude + * @param string $longitude + * @param string $sort + * + * @return \stdClass + */ + public function getPostcodesByCoordinates($latitude, $longitude, $sort = self::POSTCODES_SORT_DISTANCE) + { + return $this->get('/postcodes/', [ + 'coords' => [ + 'latitude' => $latitude, + 'longitude' => $longitude + ], + 'sort' => $sort + ]); } /** * @param string $path * @param array $queryParams * - * @return \StdClass + * @return \stdClass * * @throws RequestException */ private function get($path, array $queryParams = array()) { - $url = self::BASE_URI . $path; - - $request = $this->createHttpRequest('GET', $url, $queryParams); + $request = $this->createHttpRequest('GET', sprintf('%s%s/%s%s', $this->uriScheme, $this->domain, $this->version, $path), + $queryParams + ); $response = $this->httpClient->send($request); @@ -76,7 +114,7 @@ private function get($path, array $queryParams = array()) /** * @param ResponseInterface $response * - * @return \StdClass + * @return \stdClass * * @throws CouldNotParseResponseException */ @@ -85,7 +123,7 @@ private function parseResponse(ResponseInterface $response) $out = json_decode((string) $response->getBody()); if (json_last_error() !== JSON_ERROR_NONE) { - throw new CouldNotParseResponseException('Could not parse repsonse', $response); + throw new CouldNotParseResponseException('Could not parse response', $response); } return $out; diff --git a/tests/FH/PostcodeAPI/ClientTest.php b/tests/FH/PostcodeAPI/ClientTest.php index 8f7b729..bb6fddd 100755 --- a/tests/FH/PostcodeAPI/ClientTest.php +++ b/tests/FH/PostcodeAPI/ClientTest.php @@ -43,12 +43,12 @@ public function testRequestExceptionIsThrownWhenUsingAnInvalidApiKey() try { $client->getAddresses(); - $this->fail('Should not get to this point as an exception should have been thrown because the api client was supplied with an invalid API key'); + static::fail('Should not get to this point as an exception should have been thrown because the api client was supplied with an invalid API key'); } catch (RequestException $exception) { if ($exception->getResponse() instanceof Response) { - $this->assertTrue($exception->getResponse()->getStatusCode() === 401); + static::assertTrue($exception->getResponse()->getStatusCode() === 401); } else { - $this->fail($exception->getMessage()); + static::fail($exception->getMessage()); } } } @@ -65,7 +65,7 @@ public function testListResourceReturnsAllAddressesWhenNoParamsAreSupplied() $addresses = $response->_embedded->addresses; - $this->assertTrue(count($addresses) > 0, 'Expecting that there are always addresses available'); + static::assertGreaterThan(0, count($addresses), 'Expecting that there are always addresses available'); $this->applyAddressFieldAreSetAndOfTheCorrectTypeAssertions($addresses[0]); } @@ -82,7 +82,7 @@ public function testListResourceReturnsExpectedAddressWhenPostcodeAndNumberAreSu $addresses = $response->_embedded->addresses; - $this->assertTrue(count($addresses) > 0, 'Expecting that there are always addresses available when no filters are applied'); + static::assertGreaterThan(0, count($addresses), 'Expecting that there are always addresses available when no filters are applied'); $firstAddress = $addresses[0]; @@ -99,7 +99,6 @@ public function testExpectedAddressInformationIsReturnedFromDetailResource() $address = $client->getAddress(self::FRESHHEADS_ADDRESS_ID); $this->applyAddressFieldAreSetAndOfTheCorrectTypeAssertions($address); - $this->applyIsFreshheadsAddressAssertions($address); } @@ -122,64 +121,68 @@ public function testClientThrowsExceptionWhenInvalidInputIsSupplied() */ private function loadMockResponse($name) { - return file_get_contents(__DIR__ . "/../../Mock/{$name}"); + return file_get_contents(sprintf('%s/../../Mock/%s', __DIR__, $name)); } /** - * @param \StdClass $address + * @param \stdClass $address */ - private function applyIsFreshheadsAddressAssertions(\StdClass $address) + private function applyIsFreshheadsAddressAssertions(\stdClass $address) { - $this->assertSame(strtoupper($address->postcode), self::FRESHHEADS_POSTCODE, 'Incoming postcode did not match the expected postcode'); - $this->assertSame((string)$address->number, (string)self::FRESHHEADS_NUMBER, 'Incoming number did not match the expected number'); - $this->assertSame($address->city->label, self::FRESHHEADS_CITY, 'Incoming city did not match the expected city'); + static::assertSame(strtoupper($address->postcode), self::FRESHHEADS_POSTCODE, 'Incoming postcode did not match the expected postcode'); + static::assertSame((string)$address->number, (string)self::FRESHHEADS_NUMBER, 'Incoming number did not match the expected number'); + static::assertSame($address->city->label, self::FRESHHEADS_CITY, 'Incoming city did not match the expected city'); // use number_format number rounding to allow for minor changes between expected and actual value - $this->assertSame( - number_format($address->geo->center->wgs84->coordinates[0], 5), + $wgs84 = $address->geo->center->wgs84; + + static::assertSame( + number_format($wgs84->coordinates[0], 5), number_format(self::FRESHHEADS_LONGITUDE, 5), 'Incoming longitude did not match the expected value' ); - $this->assertSame( - number_format($address->geo->center->wgs84->coordinates[1], 5), + static::assertSame( + number_format($wgs84->coordinates[1], 5), number_format(self::FRESHHEADS_LATITUDE, 5), 'Incoming latitude did not match the expected value' ); } /** - * @param \StdClass $response + * @param \stdClass $response */ - private function applyAssertsToMakeSureAddressesArrayIsAvailableInResponse(\StdClass $response) + private function applyAssertsToMakeSureAddressesArrayIsAvailableInResponse(\stdClass $response) { - $this->assertTrue(isset($response->_embedded->addresses)); - $this->assertTrue(is_array($response->_embedded->addresses)); + static::assertTrue(isset($response->_embedded->addresses)); + static::assertTrue(is_array($response->_embedded->addresses)); } /** * @param \stdClass $address */ - private function applyAddressFieldAreSetAndOfTheCorrectTypeAssertions(\StdClass $address) + private function applyAddressFieldAreSetAndOfTheCorrectTypeAssertions(\stdClass $address) { // only test the availability of the most import fields and their values - $this->assertTrue(isset($address->street), 'Incoming address did not have a street field'); - $this->assertTrue(is_string($address->street), 'Incoming address did not have a street value of type string'); + static::assertTrue(isset($address->street), 'Incoming address did not have a street field'); + static::assertTrue(is_string($address->street), 'Incoming address did not have a street value of type string'); + + static::assertTrue(isset($address->city->label), 'Incoming address did not have a city.label field'); + static::assertTrue(is_string($address->city->label), 'Incoming address did not have a city.label value of type string'); - $this->assertTrue(isset($address->city->label), 'Incoming address did not have a city.label field'); - $this->assertTrue(is_string($address->city->label), 'Incoming address did not have a city.label value of type string'); + static::assertTrue(isset($address->postcode), 'Incoming address did not have a postcode field'); + static::assertTrue(preg_match(self::POSTCODE_PATTERN, $address->postcode) === 1, 'Incoming address did not have a postcode value that matches the pattern: ' . self::POSTCODE_PATTERN); - $this->assertTrue(isset($address->postcode), 'Incoming address did not have a postcode field'); - $this->assertTrue(preg_match(self::POSTCODE_PATTERN, $address->postcode) === 1, 'Incoming address did not have a postcode value that matches the pattern: ' . self::POSTCODE_PATTERN); + static::assertTrue(isset($address->number), 'Incoming address did not have a number field'); + static::assertTrue(is_string($address->number) || is_numeric($address->number), 'Incoming address did not have a number field with type string'); - $this->assertTrue(isset($address->number), 'Incoming address did not have a number field'); - $this->assertTrue(is_string($address->number) || is_numeric($address->number), 'Incoming address did not have a number field with type string'); + $wgs84 = $address->geo->center->wgs84; - $this->assertTrue(isset($address->geo->center->wgs84->coordinates[0]), 'Incoming address did not have a longitude field'); - $this->assertTrue(is_float($address->geo->center->wgs84->coordinates[0]), 'Incoming address did not have a longitude value of type float'); + static::assertTrue(isset($wgs84->coordinates[0]), 'Incoming address did not have a longitude field'); + static::assertTrue(is_float($address->geo->center->wgs84->coordinates[0]), 'Incoming address did not have a longitude value of type float'); - $this->assertTrue(isset($address->geo->center->wgs84->coordinates[1]), 'Incoming address did not have a latitude field'); - $this->assertTrue(is_float($address->geo->center->wgs84->coordinates[1]), 'Incoming address did not have a latitude value of type float'); + static::assertTrue(isset($wgs84->coordinates[1]), 'Incoming address did not have a latitude field'); + static::assertTrue(is_float($address->geo->center->wgs84->coordinates[1]), 'Incoming address did not have a latitude value of type float'); } /** From bf19113e83941ab86da3e3c300da3132c3c64d66 Mon Sep 17 00:00:00 2001 From: Evert Harmeling Date: Wed, 4 Jan 2017 10:56:52 +0100 Subject: [PATCH 4/4] Switched to httplug implementation --- .travis.yml | 3 +- CHANGELOG.md | 3 +- README.md | 49 ++++++++++- composer.json | 12 ++- lib/FH/PostcodeAPI/Client.php | 87 +++++++++++-------- .../CouldNotParseResponseException.php | 2 +- .../Exception/InvalidApiKeyException.php | 10 +++ .../PostcodeApiExceptionInterface.php | 10 +++ .../Exception/ServerErrorException.php | 47 ++++++++++ tests/FH/PostcodeAPI/ClientTest.php | 39 +++------ 10 files changed, 189 insertions(+), 73 deletions(-) create mode 100644 lib/FH/PostcodeAPI/Exception/InvalidApiKeyException.php create mode 100644 lib/FH/PostcodeAPI/Exception/PostcodeApiExceptionInterface.php create mode 100644 lib/FH/PostcodeAPI/Exception/ServerErrorException.php diff --git a/.travis.yml b/.travis.yml index f96d9c7..27e3edc 100755 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,10 @@ php: - 5.5 - 5.6 - 7.0 + - 7.1 before_script: - - composer install --dev + - composer install script: phpunit --coverage-text diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f23472..7a48bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # CHANGELOG -## 2.1.0 +## 3.0.0 +* Dropped direct usage of Guzzle and now make use of [HTTPPlug](http://httplug.io/), see README. * Added `getPostcodes()` call, to get postcodes based on provided `latitude, longitude`. Note that this call is only available with a premium account. ## 2.0.0 diff --git a/README.md b/README.md index facfd4b..b4ed5c8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ by [Freshheads](https://www.freshheads.com) and will be maintained in sync with Requirements ------------ -FHPostcodeAPIClient works with PHP 5.4.0 or up. This library is dependent on the awesome [Guzzle](http://guzzlephp.org/) HTTP client library. +FHPostcodeAPIClient works with PHP 5.5.0 or up. This library depends on the [HTTPPlug](http://httplug.io/), see http://docs.php-http.org/en/latest/httplug/introduction.html. Installation ------------ @@ -36,7 +36,16 @@ require_once 'vendor/autoload.php'; // initiate client $apiKey = 'replace_with_your_own_api_key'; -$client = new \FH\PostcodeAPI\Client(new \GuzzleHttp\Client(), $apiKey); +// In this example we made use of the Guzzle6 as HTTPClient in combination with an HTTPPlug compatible adapter. +$client = new \FH\PostcodeAPI\Client( + new Http\Adapter\Guzzle6\Client( + new GuzzleHttp\Client([ + 'headers' => [ + 'X-Api-Key' => $apiKey + ] + ]) + ) +); // call endpoints $response = $client->getAddresses('5041EB', 21); @@ -46,4 +55,38 @@ $response = $client->getAddress('0855200000061001'); $response = $client->getPostcodes('51.566405', '5.077171'); ``` -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/freshheads/fhpostcodeapiclient/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +Note that to be able to run the example above you should have ran the following command, to have Guzzle6 and the Adapter available. + +```bash +composer require php-http/guzzle6-adapter +``` + +Within Symfony project +---------------------- + +We recommend to use [Guzzle](https://github.com/guzzle/guzzle), to be able to use Guzzle in combination with the PostcodeApiClient you should also make use of the +[Guzzle6Adapter](https://github.com/php-http/guzzle6-adapter). By running the following command you automatically install Guzzle aswel. + +```bash +composer require php-http/guzzle6-adapter +``` + +And add the following service definitions: +```yaml +project.http.guzzle.client: + class: GuzzleHttp\Client + arguments: + - { headers: { X-Api-Key: 'replace_with_your_own_api_key' } } + +project.http.adapter.guzzle.client: + class: Http\Adapter\Guzzle6\Client + arguments: + - '@project.http.guzzle.client' + +project.client.postal_code: + class: FH\PostcodeAPI\Client + arguments: + - '@project.http.adapter.guzzle.client' +``` + +You should now be able use the `project.client.postal_code` service to make requests to the PostcodeAPI. diff --git a/composer.json b/composer.json index a2149c8..242dd97 100755 --- a/composer.json +++ b/composer.json @@ -17,10 +17,15 @@ ], "require": { "php": ">=5.4.0", - "guzzlehttp/guzzle": "^6.0" + "php-http/httplug": "^1.1", + "guzzlehttp/psr7": "^1.3" }, "require-dev": { - "phpunit/phpunit": "^4.3.5" + "phpunit/phpunit": "^4.3.5|^5.0", + "php-http/mock-client": "^0.3.0" + }, + "suggest": { + "php-http/guzzle6-adapter": "An HTTPlug adapter for the Guzzle 6 HTTP client" }, "autoload": { "psr-0": { "FH\\PostcodeAPI": "lib/" } @@ -29,5 +34,8 @@ "branch-alias": { "dev-master": "3.0.x-dev" } + }, + "scripts": { + "test": "./vendor/phpunit/phpunit/phpunit --coverage-text" } } diff --git a/lib/FH/PostcodeAPI/Client.php b/lib/FH/PostcodeAPI/Client.php index 73eb29e..32d631f 100755 --- a/lib/FH/PostcodeAPI/Client.php +++ b/lib/FH/PostcodeAPI/Client.php @@ -3,46 +3,43 @@ namespace FH\PostcodeAPI; use FH\PostcodeAPI\Exception\CouldNotParseResponseException; -use GuzzleHttp\Client as HTTPClient; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Exception\RequestException; +use FH\PostcodeAPI\Exception\InvalidApiKeyException; +use FH\PostcodeAPI\Exception\ServerErrorException; use GuzzleHttp\Psr7\Request; +use Http\Client\HttpClient; +use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; /** * Client library for postcodeapi.nu 2.0 web service. * * @author Gijs Nieuwenhuis + * @author Evert Harmeling */ class Client { const POSTCODES_SORT_DISTANCE = 'distance'; - /** - * @var string - */ - private $uriScheme = 'https://'; - /** * @var null|string */ - private $domain = 'postcode-api.apiwise.nl'; + private $url = 'https://postcode-api.apiwise.nl'; /** * @var string */ - private $version = 'v2'; + private $version = 'v2'; /** - * @var HTTPClient + * @var HttpClient */ private $httpClient; - public function __construct(ClientInterface $httpClient, $domain = null) + public function __construct(HttpClient $httpClient, $url = null) { - if (null !== $domain) { - $this->domain = $domain; + if (null !== $url) { + $this->url = $url; } $this->httpClient = $httpClient; @@ -94,39 +91,28 @@ public function getPostcodesByCoordinates($latitude, $longitude, $sort = self::P /** * @param string $path - * @param array $queryParams + * @param array $params * * @return \stdClass * * @throws RequestException */ - private function get($path, array $queryParams = array()) + private function get($path, array $params = []) { - $request = $this->createHttpRequest('GET', sprintf('%s%s/%s%s', $this->uriScheme, $this->domain, $this->version, $path), - $queryParams - ); + $request = $this->createHttpGetRequest($this->buildUrl($path), $params); - $response = $this->httpClient->send($request); + $response = $this->httpClient->sendRequest($request); - return $this->parseResponse($response); + return $this->parseResponse($response, $request); } /** - * @param ResponseInterface $response - * - * @return \stdClass - * - * @throws CouldNotParseResponseException + * @param string $path + * @return string */ - private function parseResponse(ResponseInterface $response) + private function buildUrl($path) { - $out = json_decode((string) $response->getBody()); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw new CouldNotParseResponseException('Could not parse response', $response); - } - - return $out; + return sprintf('%s/%s%s', $this->url, $this->version, $path); } /** @@ -136,10 +122,37 @@ private function parseResponse(ResponseInterface $response) * * @return Request */ - private function createHttpRequest($method, $url, array $queryParams = array()) + private function createHttpGetRequest($url, array $params = []) { - $url = $url . (count($queryParams) > 0 ? '?' . http_build_query($queryParams) : ''); + $url .= (count($params) > 0 ? '?' . http_build_query($params, null, '&', PHP_QUERY_RFC3986) : ''); + + return new Request('GET', $url); + } + + /** + * @param ResponseInterface $response + * + * @return \stdClass + * + * @throws CouldNotParseResponseException + */ + private function parseResponse(ResponseInterface $response, RequestInterface $request) + { + $result = json_decode((string) $response->getBody()->getContents()); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new CouldNotParseResponseException('Could not parse response', $response); + } + + if (property_exists($result, 'error')) { + switch ($result->error) { + case 'API key is invalid.': + throw new InvalidApiKeyException(); + case 'An unknown server error occured.': + throw ServerErrorException::fromRequest($request); + } + } - return new Request($method, $url); + return $result; } } diff --git a/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php b/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php index 837544b..0bf2edb 100644 --- a/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php +++ b/lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php @@ -7,7 +7,7 @@ /** * @author Gijs Nieuwenhuis */ -final class CouldNotParseResponseException extends \Exception +final class CouldNotParseResponseException extends \Exception implements PostcodeApiExceptionInterface { /** * @var ResponseInterface diff --git a/lib/FH/PostcodeAPI/Exception/InvalidApiKeyException.php b/lib/FH/PostcodeAPI/Exception/InvalidApiKeyException.php new file mode 100644 index 0000000..79d776d --- /dev/null +++ b/lib/FH/PostcodeAPI/Exception/InvalidApiKeyException.php @@ -0,0 +1,10 @@ + + */ +class InvalidApiKeyException extends \Exception implements PostcodeApiExceptionInterface +{ +} diff --git a/lib/FH/PostcodeAPI/Exception/PostcodeApiExceptionInterface.php b/lib/FH/PostcodeAPI/Exception/PostcodeApiExceptionInterface.php new file mode 100644 index 0000000..a3741a6 --- /dev/null +++ b/lib/FH/PostcodeAPI/Exception/PostcodeApiExceptionInterface.php @@ -0,0 +1,10 @@ + + */ +interface PostcodeApiExceptionInterface +{ +} diff --git a/lib/FH/PostcodeAPI/Exception/ServerErrorException.php b/lib/FH/PostcodeAPI/Exception/ServerErrorException.php new file mode 100644 index 0000000..569c84a --- /dev/null +++ b/lib/FH/PostcodeAPI/Exception/ServerErrorException.php @@ -0,0 +1,47 @@ + + */ +class ServerErrorException extends \Exception implements PostcodeApiExceptionInterface +{ + /** + * @var RequestInterface + */ + private $request; + + /** + * @param RequestInterface $request + * @return ServerErrorException + */ + public function setRequest($request) + { + $this->request = $request; + + return $this; + } + + /** + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param RequestInterface $request + * @return ServerErrorException + */ + public static function fromRequest(RequestInterface $request) + { + $self = new self(); + $self->setRequest($request); + + return $self; + } +} diff --git a/tests/FH/PostcodeAPI/ClientTest.php b/tests/FH/PostcodeAPI/ClientTest.php index bb6fddd..86dc356 100755 --- a/tests/FH/PostcodeAPI/ClientTest.php +++ b/tests/FH/PostcodeAPI/ClientTest.php @@ -3,10 +3,7 @@ namespace FH\PostcodeAPI\Test; use FH\PostcodeAPI\Client; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Client AS HTTPClient; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Subscriber\Mock; +use GuzzleHttp\Psr7\Response; /** * @author Gijs Nieuwenhuis @@ -34,23 +31,16 @@ final class ClientTest extends \PHPUnit_Framework_TestCase /** @var string */ const FRESHHEADS_ADDRESS_ID = '0855200000061001'; + /** + * @expectedException FH\PostcodeAPI\Exception\InvalidApiKeyException + */ public function testRequestExceptionIsThrownWhenUsingAnInvalidApiKey() { $client = $this->createClient( $this->loadMockResponse('failed_list_with_invalid_api_key') ); - try { - $client->getAddresses(); - - static::fail('Should not get to this point as an exception should have been thrown because the api client was supplied with an invalid API key'); - } catch (RequestException $exception) { - if ($exception->getResponse() instanceof Response) { - static::assertTrue($exception->getResponse()->getStatusCode() === 401); - } else { - static::fail($exception->getMessage()); - } - } + $client->getAddresses(); } public function testListResourceReturnsAllAddressesWhenNoParamsAreSupplied() @@ -103,7 +93,7 @@ public function testExpectedAddressInformationIsReturnedFromDetailResource() } /** - * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedException FH\PostcodeAPI\Exception\ServerErrorException */ public function testClientThrowsExceptionWhenInvalidInputIsSupplied() { @@ -121,7 +111,7 @@ public function testClientThrowsExceptionWhenInvalidInputIsSupplied() */ private function loadMockResponse($name) { - return file_get_contents(sprintf('%s/../../Mock/%s', __DIR__, $name)); + return \GuzzleHttp\Psr7\parse_response(file_get_contents(__DIR__ . "/../../Mock/{$name}")); } /** @@ -190,18 +180,11 @@ private function applyAddressFieldAreSetAndOfTheCorrectTypeAssertions(\stdClass * * @return Client */ - private function createClient($mockedResponses) + private function createClient(Response $mockedResponse) { - $someKey = 'SomeApiKey'; - - $httpClient = new HTTPClient(); - - $httpClient->getEmitter()->attach( - new Mock([ - $mockedResponses - ]) - ); + $httpClient = new \Http\Mock\Client(); + $httpClient->addResponse($mockedResponse); - return new Client($httpClient, $someKey); + return new Client($httpClient); } }