diff --git a/.docker/php/Dockerfile b/.docker/php/Dockerfile
new file mode 100644
index 0000000..9856727
--- /dev/null
+++ b/.docker/php/Dockerfile
@@ -0,0 +1,23 @@
+ARG PHP_IMAGE="php:8.3"
+FROM $PHP_IMAGE
+ARG PHP_IMAGE
+
+RUN apt-get update
+
+RUN apt autoremove -y
+RUN apt-get install libicu-dev zip -y --no-install-recommends
+RUN apt-get install libmcrypt-dev -y --no-install-recommends
+RUN apt-get install git unzip -y
+RUN docker-php-ext-install mysqli pdo_mysql bcmath
+
+COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+
+RUN apt-get install zlib1g-dev libzip-dev -y --no-install-recommends
+RUN docker-php-ext-install zip
+
+RUN pecl install xdebug
+RUN docker-php-ext-enable xdebug
+
+ENV XDEBUG_MODE=coverage
+
+RUN rm -rf /var/lib/apt/lists/*
diff --git a/.github/workflows/.github-actions.yml b/.github/workflows/.github-actions.yml
new file mode 100644
index 0000000..48a1d58
--- /dev/null
+++ b/.github/workflows/.github-actions.yml
@@ -0,0 +1,80 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ - dev
+ tags:
+ - 1.*
+ - 2.*
+ pull_request:
+ branches: [ master ]
+
+ workflow_dispatch:
+
+jobs:
+ phpcs:
+ strategy:
+ matrix:
+ version: ['8.1', '8.2', '8.3']
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout the repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Setup PHP with composer v2
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.version }}
+ tools: composer:v2
+
+ - name: Install composer packages
+ run: |
+ php -v
+ composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
+
+ - name: Execute PHP_CodeSniffer
+ run: |
+ php -v
+ composer phpcs
+
+ phpunit:
+ strategy:
+ matrix:
+ version: ['8.1', '8.2', '8.3']
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout the repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.version }}
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, exif, iconv
+ coverage: xdebug
+
+ - name: Install composer packages
+ run: |
+ php -v
+ composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
+
+ - name: Execute tests
+ run: |
+ php -v
+ ./vendor/phpunit/phpunit/phpunit --version
+ ./vendor/phpunit/phpunit/phpunit --coverage-clover=coverage.xml
+ export CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}
+ bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'
+
+ - name: Upload code coverage
+ run: |
+ export CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}
+ bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'
diff --git a/.gitignore b/.gitignore
index 9c0015d..be11935 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,6 @@ bootstrap/cache/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
-!.vscode/extensions.json
\ No newline at end of file
+!.vscode/extensions.json
+
+composer.lock
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..520ea20
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,130 @@
+stages:
+ - docker
+ - build
+ - lint
+ - test
+
+# ========= Anchors (Partials) =========
+.php_base: &php_base
+ image: $CI_REGISTRY_IMAGE/php:8.3
+ tags:
+ - docker
+
+.test_base: &test_base
+ stage: test
+ before_script:
+ - rm -rf vendor
+ - php -d memory_limit=-1 /usr/bin/composer require --dev phpunit/phpunit:^$PHPUNIT_VERSION
+ - php -d memory_limit=-1 /usr/bin/composer install
+ script:
+ - composer du
+ - composer test:coverage
+ artifacts:
+ when: on_failure
+ expire_in: 4 hours
+ paths:
+ - storage/logs/
+ reports:
+ junit: coverage/phpunit.junit.xml
+ coverage_report:
+ coverage_format: cobertura
+ path: coverage/cobertura.xml
+ coverage: /^\s*Lines:\s*\d+.\d+\%/
+ tags:
+ - docker
+
+.docker_base: &docker_base
+ stage: docker
+ image: docker:20.10.8-alpine3.14
+ services:
+ - docker:20.10.8-dind-alpine3.14
+ before_script:
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+ script:
+ - docker pull $CONTAINER_IMAGE:$PHP_VERSION || true
+ - docker build --cache-from $CONTAINER_IMAGE:$PHP_VERSION --build-arg PHP_IMAGE=php:$PHP_VERSION -t $CONTAINER_IMAGE:$PHP_VERSION ./.docker/$SERVICE_NAME
+ - docker push $CONTAINER_IMAGE:$PHP_VERSION
+ when: manual
+ tags:
+ - docker
+
+.docker_variables: &docker_variables
+ CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/$SERVICE_NAME
+ DOCKER_DRIVER: devicemapper
+ DOCKER_TLS_CERTDIR: ''
+ GIT_STRATEGY: fetch
+ GIT_DEPTH: "1"
+
+# ======================
+# Docker jobs
+# ======================
+docker:php81:
+ <<: *docker_base
+ variables:
+ SERVICE_NAME: php
+ PHP_VERSION: "8.1"
+ <<: *docker_variables
+
+docker:php82:
+ <<: *docker_base
+ variables:
+ SERVICE_NAME: php
+ PHP_VERSION: "8.2"
+ <<: *docker_variables
+
+docker:php83:
+ <<: *docker_base
+ variables:
+ SERVICE_NAME: php
+ PHP_VERSION: "8.3"
+ <<: *docker_variables
+
+# ======================
+# Build jobs
+# ======================
+build:php:
+ <<: *php_base
+ stage: build
+ script:
+ - composer install
+ artifacts:
+ paths:
+ - vendor/
+ expire_in: 2 hours
+ cache:
+ paths:
+ - vendor
+ key: php
+
+# ======================
+# Lint jobs
+# ======================
+lint:php:
+ <<: *php_base
+ stage: lint
+ script:
+ - composer phpcs
+ dependencies:
+ - build:php
+ allow_failure: true
+
+# ======================
+# Test jobs
+# ======================
+test:php81:
+ <<: *test_base
+ image: $CI_REGISTRY_IMAGE/php:8.1
+ variables:
+ PHPUNIT_VERSION: "9.5"
+
+test:php82:
+ <<: *test_base
+ image: $CI_REGISTRY_IMAGE/php:8.2
+ variables:
+ PHPUNIT_VERSION: "9.5"
+
+test:php83:
+ <<: *test_base
+ image: $CI_REGISTRY_IMAGE/php:8.3
+ variables:
+ PHPUNIT_VERSION: "9.5"
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index c164dd6..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-language: php
-
-php:
- - 7.3
- - 7.4
-
-env:
- global:
- - CC_TEST_REPORTER_ID=$CODECLIMATE_REPO_TOKEN
- matrix:
- - LARAVEL_VERSION=5.8.*
- - LARAVEL_VERSION=6.0.*
- - LARAVEL_VERSION=7.0.*
- - LARAVEL_VERSION=8.0.*
-
-cache:
- directories:
- - $HOME/.composer/cache
-
-matrix:
- fast_finish: true
-
-sudo: false
-
-install:
- - if [ "$LARAVEL_VERSION" != "" ]; then composer require --dev "laravel/framework:${LARAVEL_VERSION}" --no-update; fi;
- - travis_retry composer install --no-interaction --prefer-source --no-suggest
-
-before_script:
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- - chmod +x ./cc-test-reporter
- - ./cc-test-reporter before-build
-
-script:
- - $TRAVIS_BUILD_DIR/vendor/bin/phpunit --coverage-clover build/logs/clover.xml
-
-after_script:
- - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT;
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26c5cab..ab19f02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,3 +15,19 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
## [1.0.2] - 14-02-2022
- Laravel 9 Support
+
+## [1.0.3] - 06-03-2023
+- Laravel 10 Support
+
+## [2.0.0] - 22-02-2024
+### Added
+- Autocomplete and get endpoint support.
+- Complete test coverage.
+
+### Changed
+- Refactored codebase, make use of `Http` facade to assist testing.
+- Moved core HTTP logic into `Laralabs\GetAddress\Http\Client` which provides `get()` and `post()`. Can be used if wanting to experiment with new endpoints or endpoints not supported by the package.
+
+### Removed
+- Unused classes.
+
diff --git a/README.md b/README.md
index 3d7bc0f..cf1295b 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
-
-
-
-
+
+
# Laravel getAddress.io Package
@@ -35,22 +33,33 @@ php artisan migrate
### Usage
A helper function and facade is available, choose your preferred method.
-Perform a lookup:
+The facade is located at `Laralabs\GetAddress\Facades\GetAddress`
+
+The endpoints currently supported are (support for other endpoints coming soon):
+- find
+- autocomplete
+- get
+
+Perform a lookup using the `find` endpoint:
```php
$results = get_address()->find($postcode, $property);
```
-Perform an expanded lookup:
+The `$property` argument is optional, just searching for a postcode will return all results for that postcode and also cache the data if the cache has been enabled.
+
+Perform a looking using the `autocomplete` endpoint:
```php
-$results = get_address()->expand()->find($postcode, $property);
+$results = get_address()->autocomplete($searchTerm, ['top' => '20']);
```
-The `$property` argument is optional, just searching for a postcode will return all results for that postcode and also cache the data if the cache has been enabled.
+The second argument supports an array of parameters to send with the POST request, the example above shows setting the returned results to the maximum allowed of 20.
-## :pushpin: Todo
+Perform a lookup using the `get` endpoint:
+```php
+$results = get_address()->get($addressId);
+```
-- Admin Features.
-- Unit Tests.
+This is used with the `autocomplete` endpoint to return the full address information, the `$addressId` argument must be the ID returned from the autocomplete response.
## :speech_balloon: Support
diff --git a/composer.json b/composer.json
index 228dd6a..4c90d45 100644
--- a/composer.json
+++ b/composer.json
@@ -10,23 +10,38 @@
],
"minimum-stability": "dev",
"require": {
- "php": "^7.3|^8.0",
- "illuminate/support": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0"
+ "php": "^8.1",
+ "guzzlehttp/guzzle": "^7.2",
+ "laravel/framework": "^10.0"
},
"require-dev": {
+ "roave/security-advisories": "dev-latest",
"mockery/mockery": "^1.0",
- "orchestra/testbench": "^3.4|^4.0|^5.0|^6.0|^7.0",
- "phpunit/phpunit": ">6.0"
+ "orchestra/testbench": "^8.0",
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "^3.7",
+ "clntdev/coding-standards": "^1.1.0",
+ "spatie/phpunit-snapshot-assertions": "^4.0"
},
"autoload": {
"psr-4": {
"Laralabs\\GetAddress\\": "src/",
+ "Laralabs\\GetAddress\\Factories\\": "database/factories",
"Laralabs\\GetAddress\\Tests\\": "tests/"
},
"files": [
"src/Helpers/functions.php"
]
},
+ "scripts": {
+ "test": "./vendor/bin/phpunit",
+ "test:coverage": [
+ "@putenv XDEBUG_MODE=coverage",
+ "vendor/bin/phpunit --log-junit=coverage/phpunit.junit.xml --coverage-cobertura=coverage/cobertura.xml --coverage-text"
+ ],
+ "phpcs": "vendor/bin/phpcs ./src ./tests --runtime-set ignore_warnings_on_exit true --standard=./vendor/clntdev/coding-standards/phpcs.xml",
+ "cbf": "vendor/bin/phpcbf ./src ./tests --standard=./vendor/clntdev/coding-standards/phpcs.xml"
+ },
"extra": {
"laravel": {
"providers": [
@@ -36,5 +51,10 @@
"GetAddress": "Laralabs\\GetAddress\\Facades\\GetAddress"
}
}
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
}
- }
+}
diff --git a/database/factories/CachedAddressFactory.php b/database/factories/CachedAddressFactory.php
new file mode 100644
index 0000000..3eabdb1
--- /dev/null
+++ b/database/factories/CachedAddressFactory.php
@@ -0,0 +1,25 @@
+ '123 Example Street',
+ 'line_2' => '',
+ 'line_3' => '',
+ 'line_4' => '',
+ 'locality' => 'Moseley',
+ 'town_or_city' => 'Birmingham',
+ 'county' => 'West Midlands',
+ 'postcode' => 'B13 9SZ',
+ ];
+ }
+}
\ No newline at end of file
diff --git a/database/migrations/2019_03_09_000000_create_getaddress_cache_table.php b/database/migrations/2019_03_09_000000_create_getaddress_cache_table.php
index 1ec0a00..3ad39bd 100644
--- a/database/migrations/2019_03_09_000000_create_getaddress_cache_table.php
+++ b/database/migrations/2019_03_09_000000_create_getaddress_cache_table.php
@@ -4,16 +4,11 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreateGetAddressCacheTable extends Migration
+return new class extends Migration
{
- /**
- * Run the migrations.
- *
- * @return void
- */
- public function up()
+ public function up(): void
{
- Schema::create('getaddress_cache', function (Blueprint $table) {
+ Schema::create('getaddress_cache', static function (Blueprint $table): void {
$table->bigIncrements('id');
$table->string('line_1')->index();
$table->string('line_2')->nullable();
@@ -37,13 +32,8 @@ public function up()
});
}
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
+ public function down(): void
{
Schema::dropIfExists('getaddress_cache');
}
-}
+};
diff --git a/phpunit.xml b/phpunit.xml
index 7ece024..6e05b21 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -29,6 +29,10 @@
+
+
+
+
diff --git a/src/Cache/Manager.php b/src/Cache/Manager.php
index 68b3f65..03c40f6 100644
--- a/src/Cache/Manager.php
+++ b/src/Cache/Manager.php
@@ -2,6 +2,7 @@
namespace Laralabs\GetAddress\Cache;
+use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Laralabs\GetAddress\Models\CachedAddress;
use Laralabs\GetAddress\Responses\Address;
@@ -10,15 +11,9 @@
class Manager
{
- /**
- * @var int
- */
- protected $expiry;
+ protected int $expiry = 30;
- /**
- * @var bool
- */
- protected $expand;
+ protected bool $expand = false;
public function __construct()
{
@@ -26,20 +21,16 @@ public function __construct()
$this->expand = config('getaddress.expanded_results');
}
- /**
- * @param $postcode
- * @param $property
- *
- * @return array|null
- */
- public function checkCache($postcode, $property): ?array
+ public function checkCache(string $postcode, null|string|int $property): ?array
{
- $params = ['postcode' => $postcode, 'property' => $property];
-
- $results = CachedAddress::where(function ($query) use ($params) {
- $params['property'] !== null ? $query->where('postcode', '=', $params['postcode'])->where('line_1', 'LIKE', '%'.$params['property'].'%')
- : $query->where('postcode', '=', $params['postcode']);
- })->get();
+ $results = CachedAddress::query()->when(
+ filled($property),
+ static fn (Builder $query): Builder => $query->where(
+ 'line_1',
+ 'LIKE',
+ '%'.$property.'%'
+ )
+ )->where('postcode', $postcode)->get();
if (count($results) >= 1) {
return $this->checkExpiry($results);
@@ -48,13 +39,6 @@ public function checkCache($postcode, $property): ?array
return null;
}
- /**
- * Store response in cache.
- *
- * @param AddressCollectionResponse $response
- *
- * @return AddressCollectionResponse
- */
public function responseToCache(AddressCollectionResponse $response): AddressCollectionResponse
{
foreach ($response->getAddresses() as $address) {
@@ -67,24 +51,28 @@ public function responseToCache(AddressCollectionResponse $response): AddressCol
]));
}
- if ($address instanceof Address) {
- CachedAddress::create(array_merge($address->toArray(), [
- 'longitude' => $response->getLongitude(),
- 'latitude' => $response->getLatitude(),
- 'postcode' => $response->getPostcode(),
- 'expanded_result' => false,
- ]));
+ if ($address instanceof Address === false) {
+ continue;
}
+
+ CachedAddress::create(array_merge($address->toArray(), [
+ 'longitude' => $response->getLongitude(),
+ 'latitude' => $response->getLatitude(),
+ 'postcode' => $response->getPostcode(),
+ 'expanded_result' => false,
+ ]));
}
return $response;
}
- /**
- * @param Collection $results
- *
- * @return array|null
- */
+ public function expand(bool $expand = true): self
+ {
+ $this->expand = $expand;
+
+ return $this;
+ }
+
protected function checkExpiry(Collection $results): ?array
{
$address = $results->first();
@@ -98,25 +86,21 @@ protected function checkExpiry(Collection $results): ?array
return $this->formatCachedAddresses($results);
}
- /**
- * @param Collection $results
- *
- * @return array
- */
protected function formatCachedAddresses(Collection $results): array
{
return [
+ 'postcode' => $results->first()->postcode,
'longitude' => (float) $results->first()->longitude,
'latitude' => (float) $results->first()->latitude,
- 'addresses' => $results->map(function ($address) {
- if ($this->expand) {
- return array_merge([
- 'formatted_string' => $address->formatted_string,
- 'formatted_address' => array_values($address->only(CachedAddress::$fields)),
- ], $address->only(CachedAddress::$expandedFields));
+ 'addresses' => $results->transform(function (CachedAddress $address): string|array {
+ if ($this->expand === false) {
+ return $address->formatted_string;
}
- return $address->formatted_string;
+ return array_merge([
+ 'formatted_string' => $address->formatted_string,
+ 'formatted_address' => array_values($address->only(CachedAddress::$fields)),
+ ], $address->only(CachedAddress::$expandedFields));
})->toArray(),
];
}
diff --git a/src/Exceptions/ForbiddenException.php b/src/Exceptions/Forbidden.php
similarity index 52%
rename from src/Exceptions/ForbiddenException.php
rename to src/Exceptions/Forbidden.php
index d146021..4f44691 100644
--- a/src/Exceptions/ForbiddenException.php
+++ b/src/Exceptions/Forbidden.php
@@ -2,12 +2,9 @@
namespace Laralabs\GetAddress\Exceptions;
-class ForbiddenException extends \Exception
+use Exception;
+
+class Forbidden extends Exception
{
- /**
- * Exception Message.
- *
- * @var string
- */
protected $message = 'Your API key is not valid for this request.';
}
diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php
new file mode 100644
index 0000000..8637693
--- /dev/null
+++ b/src/Exceptions/Handler.php
@@ -0,0 +1,26 @@
+ new InvalidPostcode(),
+ 401 => new Forbidden(),
+ 404 => new PostcodeNotFound(),
+ 429 => new TooManyRequests(),
+ 500 => new ServerError(),
+ default => new Unknown($statusCode),
+ };
+ }
+}
diff --git a/src/Exceptions/InvalidPostcodeException.php b/src/Exceptions/InvalidPostcode.php
similarity index 52%
rename from src/Exceptions/InvalidPostcodeException.php
rename to src/Exceptions/InvalidPostcode.php
index 8b736da..5bcbeb1 100644
--- a/src/Exceptions/InvalidPostcodeException.php
+++ b/src/Exceptions/InvalidPostcode.php
@@ -2,12 +2,9 @@
namespace Laralabs\GetAddress\Exceptions;
-class InvalidPostcodeException extends \Exception
+use Exception;
+
+class InvalidPostcode extends Exception
{
- /**
- * Exception Message.
- *
- * @var string
- */
protected $message = 'The postcode you provided was not a valid format.';
}
diff --git a/src/Exceptions/PostcodeNotFoundException.php b/src/Exceptions/PostcodeNotFound.php
similarity index 51%
rename from src/Exceptions/PostcodeNotFoundException.php
rename to src/Exceptions/PostcodeNotFound.php
index 326ba69..d8c7e85 100644
--- a/src/Exceptions/PostcodeNotFoundException.php
+++ b/src/Exceptions/PostcodeNotFound.php
@@ -2,12 +2,9 @@
namespace Laralabs\GetAddress\Exceptions;
-class PostcodeNotFoundException extends \Exception
+use Exception;
+
+class PostcodeNotFound extends Exception
{
- /**
- * Exception Message.
- *
- * @var string
- */
protected $message = 'The postcode you provided could not be found.';
}
diff --git a/src/Exceptions/ServerException.php b/src/Exceptions/ServerError.php
similarity index 53%
rename from src/Exceptions/ServerException.php
rename to src/Exceptions/ServerError.php
index c62837c..7b03a32 100644
--- a/src/Exceptions/ServerException.php
+++ b/src/Exceptions/ServerError.php
@@ -2,12 +2,9 @@
namespace Laralabs\GetAddress\Exceptions;
-class ServerException extends \Exception
+use Exception;
+
+class ServerError extends Exception
{
- /**
- * Exception Message.
- *
- * @var string
- */
protected $message = 'getAddress.io is currently having issues.';
}
diff --git a/src/Exceptions/TooManyRequestsException.php b/src/Exceptions/TooManyRequests.php
similarity index 52%
rename from src/Exceptions/TooManyRequestsException.php
rename to src/Exceptions/TooManyRequests.php
index 95b4312..dd5dd33 100644
--- a/src/Exceptions/TooManyRequestsException.php
+++ b/src/Exceptions/TooManyRequests.php
@@ -2,12 +2,9 @@
namespace Laralabs\GetAddress\Exceptions;
-class TooManyRequestsException extends \Exception
+use Exception;
+
+class TooManyRequests extends Exception
{
- /**
- * Exception Message.
- *
- * @var string
- */
protected $message = 'You have made too many requests for this key.';
}
diff --git a/src/Exceptions/UnknownException.php b/src/Exceptions/Unknown.php
similarity index 57%
rename from src/Exceptions/UnknownException.php
rename to src/Exceptions/Unknown.php
index 93a39b9..09477a9 100644
--- a/src/Exceptions/UnknownException.php
+++ b/src/Exceptions/Unknown.php
@@ -2,19 +2,13 @@
namespace Laralabs\GetAddress\Exceptions;
-class UnknownException extends \Exception
+use Exception;
+
+class Unknown extends Exception
{
- /**
- * Message.
- *
- * @var string
- */
protected $message = 'getAddress responded with a %d http status';
- /**
- * {@inheritdoc}
- */
- public function __construct($status)
+ public function __construct(int $status)
{
$this->message = sprintf($this->message, $status);
diff --git a/src/Facades/GetAddress.php b/src/Facades/GetAddress.php
index cd49756..b8f29d0 100644
--- a/src/Facades/GetAddress.php
+++ b/src/Facades/GetAddress.php
@@ -4,6 +4,12 @@
use Illuminate\Support\Facades\Facade;
+/**
+ * @method static find(string $postcode, null|int|string $propertyNumber = null, bool $sortNumerically = true)
+ * @method static autocomplete(string $term, array $parameters = [])
+ * @method static get(string $id)
+ * @method static expand()
+ */
class GetAddress extends Facade
{
public static function getFacadeRoot(): \Laralabs\GetAddress\GetAddress
diff --git a/src/Facades/GetAddressAdmin.php b/src/Facades/GetAddressAdmin.php
deleted file mode 100644
index 6d857e1..0000000
--- a/src/Facades/GetAddressAdmin.php
+++ /dev/null
@@ -1,13 +0,0 @@
- null]);
- }
-}
diff --git a/src/GetAddress.php b/src/GetAddress.php
index 0747ea5..4ec6c86 100644
--- a/src/GetAddress.php
+++ b/src/GetAddress.php
@@ -3,70 +3,58 @@
namespace Laralabs\GetAddress;
use Laralabs\GetAddress\Cache\Manager;
+use Laralabs\GetAddress\Http\Client;
use Laralabs\GetAddress\Responses\Address;
use Laralabs\GetAddress\Responses\AddressCollectionResponse;
+use Laralabs\GetAddress\Responses\AutocompleteCollectionResponse;
use Laralabs\GetAddress\Responses\ExpandedAddress;
+use Laralabs\GetAddress\Responses\SingleAddressCollectionResponse;
-class GetAddress extends GetAddressBase
+class GetAddress
{
- /**
- * @var bool
- */
- protected $cache;
+ protected Client $http;
- /**
- * @var \Laralabs\GetAddress\Cache\Manager|null
- */
- protected $manager;
+ protected bool $cache = false;
- /**
- * GetAddress constructor.
- *
- * @param string|null $apiKey
- */
- public function __construct($apiKey = null)
- {
- parent::__construct($apiKey);
+ protected ?Manager $manager;
+ protected bool $expand = false;
+
+ public function __construct(Client $client)
+ {
+ $this->http = $client;
$this->cache = config('getaddress.enable_cache');
$this->manager = $this->cache ? new Manager() : null;
+ $this->expand = config('getaddress.expanded_results');
}
/**
* Find an address or range of addresses by a postcode, and optional number/string.
*
- * @param string $postcode Postcode to search for
- * @param int|string $propertyNumber Property number or name
- * @param bool $sortNumerically Sorts addresses numerically
- *
- * @throws Exceptions\ForbiddenException
- * @throws Exceptions\InvalidPostcodeException
- * @throws Exceptions\PostcodeNotFoundException
- * @throws Exceptions\ServerException
- * @throws Exceptions\TooManyRequestsException
- * @throws Exceptions\UnknownException
- *
- * @return \Laralabs\GetAddress\Responses\AddressCollectionResponse
- * @return AddressCollectionResponse
+ * @param string $postcode Postcode to search for
+ * @param null|int|string $propertyNumber Property number or name
+ * @param bool $sortNumerically Sorts addresses numerically
*/
- public function find($postcode, $propertyNumber = null, $sortNumerically = true): AddressCollectionResponse
- {
+ public function find(
+ string $postcode,
+ null|int|string $propertyNumber = null,
+ bool $sortNumerically = true
+ ): AddressCollectionResponse {
if ($this->cache) {
- $cached = $this->manager->checkCache($postcode, $propertyNumber);
+ $cached = $this->manager->expand($this->expand)->checkCache($postcode, $propertyNumber);
if ($cached !== null) {
return $this->createAddressCollectionResponse($postcode, $cached);
}
}
- $this->queryString['sort'] = (int) $sortNumerically;
-
- $url = sprintf('find/%s', $postcode);
- if ($propertyNumber !== null) {
- $url .= sprintf('/%s', $propertyNumber);
- }
-
- $response = $this->createAddressCollectionResponse($postcode, $this->call('GET', $url));
+ $response = $this->createAddressCollectionResponse(
+ $postcode,
+ $this->http->get('find', [$postcode, $propertyNumber], [
+ 'sort' => (int) $sortNumerically,
+ 'expand' => $this->expand,
+ ])
+ );
if ($this->cache && $propertyNumber === null) {
$this->manager->responseToCache($response);
@@ -76,10 +64,30 @@ public function find($postcode, $propertyNumber = null, $sortNumerically = true)
}
/**
- * Override expanded results.
+ * Get an autocomplete response for the given search term.
+ * Use 'id' returned to get full address information using: @see get()
*
- * @return self
+ * @param string $term Search term
+ * @param array $parameters Additional parameters
*/
+ public function autocomplete(string $term, array $parameters = []): AutocompleteCollectionResponse
+ {
+ return new AutocompleteCollectionResponse(
+ $this->http->post('autocomplete', $term, $parameters)['suggestions'] ?? null
+ );
+ }
+
+ /**
+ * Get the full address information for the given ID.
+ * This method should be used when using: @see autocomplete()
+ *
+ * @param string $id Address unique ID.
+ */
+ public function get(string $id): SingleAddressCollectionResponse
+ {
+ return new SingleAddressCollectionResponse($this->http->get('get', $id), $this->expand);
+ }
+
public function expand(): self
{
$this->expand = true;
@@ -87,20 +95,14 @@ public function expand(): self
return $this;
}
- /**
- * @param string $postcode
- * @param array $response
- *
- * @return AddressCollectionResponse
- */
protected function createAddressCollectionResponse(string $postcode, array $response): AddressCollectionResponse
{
return new AddressCollectionResponse(
$postcode,
$response['latitude'],
$response['longitude'],
- array_map(function ($address) {
- return $this->expand ? new ExpandedAddress($address) : new Address($address);
+ array_map(function (string|array $address): ExpandedAddress|Address {
+ return $this->expand && is_array($address) ? new ExpandedAddress($address) : new Address($address);
}, $response['addresses'])
);
}
diff --git a/src/GetAddressAdmin.php b/src/GetAddressAdmin.php
deleted file mode 100644
index 1f54a22..0000000
--- a/src/GetAddressAdmin.php
+++ /dev/null
@@ -1,11 +0,0 @@
-apiKey = $apiKey ?? config('getaddress.api_key');
- $this->adminKey = $adminKey ?? config('getaddress.admin_key');
- $this->delay = config('getaddress.limit_delay');
- $this->url = config('getaddress.url');
- $this->expand = config('getaddress.expanded_results');
- $this->http = new Client();
- }
-
- /**
- * Call an external resource.
- *
- * @param $method
- * @param $url
- * @param array $parameters
- *
- * @throws ForbiddenException
- * @throws InvalidPostcodeException
- * @throws PostcodeNotFoundException
- * @throws ServerException
- * @throws TooManyRequestsException
- * @throws UnknownException
- *
- * @return array
- */
- public function call($method, $url, array $parameters = []): array
- {
- $this->queryString['api-key'] = $this->requiresAdminKey ? $this->adminKey : $this->apiKey;
-
- $method = strtolower($method);
- $url = sprintf('%s/%s?%s', $this->url, $url, http_build_query($this->queryString));
-
- if ($this->expand) {
- $url .= '&expand=true';
- }
-
- $response = $this->http->{$method}($url, $parameters);
-
- if (floor($response->getStatusCode() / 100) > 2) {
- if ($response->getStatusCode() !== 429) {
- $this->throwException($response->getStatusCode());
- }
-
- sleep($this->delay + .25);
-
- $response = $this->http->{$method}($url, $parameters);
-
- if ($response->getStatusCode() !== 200) {
- $this->throwException($response->getStatusCode());
- }
- }
-
- return json_decode($response->getBody(), true);
- }
-
- /**
- * Throw exception.
- *
- * @param $statusCode
- *
- * @throws ForbiddenException
- * @throws InvalidPostcodeException
- * @throws PostcodeNotFoundException
- * @throws ServerException
- * @throws TooManyRequestsException
- * @throws UnknownException
- */
- protected function throwException($statusCode): void
- {
- switch ($statusCode) {
- case 400:
- throw new InvalidPostcodeException();
- case 401:
- throw new ForbiddenException();
- case 404:
- throw new PostcodeNotFoundException();
- case 429:
- throw new TooManyRequestsException();
- case 500:
- throw new ServerException();
- default:
- throw new UnknownException($statusCode);
- }
- }
-}
diff --git a/src/GetAddressServiceProvider.php b/src/GetAddressServiceProvider.php
index 10d3615..0a90b9f 100644
--- a/src/GetAddressServiceProvider.php
+++ b/src/GetAddressServiceProvider.php
@@ -2,15 +2,12 @@
namespace Laralabs\GetAddress;
+use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
+use Laralabs\GetAddress\Http\Client;
class GetAddressServiceProvider extends ServiceProvider
{
- /**
- * Register the service provider.
- *
- * @return void
- */
public function register(): void
{
$this->mergeConfigFrom(
@@ -18,20 +15,15 @@ public function register(): void
'getaddress'
);
- $this->app->bind('getaddress', function ($app, $parameters) {
- return new GetAddress($parameters['apiKey']);
+ $this->app->singleton(Client::class, function (Application $app, array $parameters): Client { //phpcs:ignore
+ return new Client($parameters['apiKey'] ?? null, $parameters['adminKey'] ?? null);
});
- $this->app->bind('getaddress-admin', function ($app, $parameters) {
- return new GetAddressAdmin($parameters['adminKey']);
+ $this->app->bind('getaddress', function (Application $app, array $parameters): GetAddress { //phpcs:ignore
+ return new GetAddress(app(Client::class, ['apiKey' => $parameters['apiKey'] ?? null]));
});
}
- /**
- * Bootstrap the application events.
- *
- * @return void
- */
public function boot(): void
{
$this->publishes([
diff --git a/src/Helpers/functions.php b/src/Helpers/functions.php
index 74f5327..5d54b13 100644
--- a/src/Helpers/functions.php
+++ b/src/Helpers/functions.php
@@ -1,19 +1,10 @@
$apiKey]);
+use Laralabs\GetAddress\GetAddress;
- return $getAddress;
- }
-}
-
-if (!function_exists('get_address_admin')) {
- function get_address_admin($adminKey = null)
+if (!function_exists('get_address')) {
+ function get_address(?string $apiKey = null): GetAddress
{
- $getAddressAdmin = app('getaddress-admin', ['adminKey' => $adminKey]);
-
- return $getAddressAdmin;
+ return app('getaddress', ['apiKey' => $apiKey]);
}
}
diff --git a/src/Http/Client.php b/src/Http/Client.php
new file mode 100644
index 0000000..be05050
--- /dev/null
+++ b/src/Http/Client.php
@@ -0,0 +1,123 @@
+apiKey = $apiKey ?? config('getaddress.api_key');
+ $this->adminKey = $adminKey ?? config('getaddress.admin_key');
+ $this->delay = config('getaddress.limit_delay');
+ $this->url = config('getaddress.url');
+ $this->expand = config('getaddress.expanded_results');
+ }
+
+ public function get(string $endpoint, string|array $term, array $queryParameters = []): ?array
+ {
+ $url = $this->buildUrl($endpoint, $term);
+ $queryParameters['api-key'] = $this->getApiKey();
+
+ if ($this->expand) {
+ $queryParameters['expand'] = true;
+ }
+
+ $response = $this->call($url, 'get', $queryParameters);
+
+ if ($response === null && $this->attempts === 0) {
+ $this->attempts = 1;
+
+ return $this->call($url, 'get', $queryParameters);
+ }
+
+ return $response;
+ }
+
+ public function post(string $endpoint, string|array $term, array $parameters = []): ?array
+ {
+ $url = $this->buildUrl($endpoint, $term);
+ $queryParameters = ['api-key' => $this->getApiKey()];
+
+ $response = $this->call($url, 'post', $queryParameters, $parameters);
+
+ if ($response === null && $this->attempts === 0) {
+ $this->attempts = 1;
+
+ return $this->call($url, 'post', $queryParameters, $parameters);
+ }
+
+ return $response;
+ }
+
+ private function call(
+ string $url,
+ string $method,
+ array $queryParameters = [],
+ array $parameters = []
+ ): mixed {
+ return $this->handleResponse(Http::withQueryParameters($queryParameters)->$method($url, $parameters ?? null));
+ }
+
+ private function handleResponse(PromiseInterface|Response $response): mixed
+ {
+ if ($response->failed() && $response->status() !== 429) {
+ Handler::throwException($response->status());
+ }
+
+ if ($response->status() === 429) {
+ sleep($this->delay + .25);
+ return null;
+ }
+
+ return $response->json();
+ }
+
+ private function getApiKey(): ?string
+ {
+ return $this->isAdminMode() ? $this->adminKey : $this->apiKey;
+ }
+
+ private function buildUrl(string $endpoint, string|array $term): string
+ {
+ if (is_array($term)) {
+ return Str::of($this->url)
+ ->append("/{$endpoint}")
+ ->append("/")
+ ->append(implode("/", array_filter($term)));
+ }
+
+ return Str::of($this->url)->append("/{$endpoint}")->append("/{$term}");
+ }
+
+ public function admin(): self
+ {
+ $this->adminMode = true;
+
+ return $this;
+ }
+
+ public function isAdminMode(): bool
+ {
+ return $this->adminMode;
+ }
+}
diff --git a/src/Models/CachedAddress.php b/src/Models/CachedAddress.php
index 89e6da9..3bfb5f1 100644
--- a/src/Models/CachedAddress.php
+++ b/src/Models/CachedAddress.php
@@ -2,18 +2,18 @@
namespace Laralabs\GetAddress\Models;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Laralabs\GetAddress\Factories\CachedAddressFactory;
class CachedAddress extends Model
{
- /**
- * @var string
- */
+ use HasFactory;
+
+ /** @var string */
protected $table = 'getaddress_cache';
- /**
- * @var array
- */
+ /** @var array */
protected $fillable = [
'line_1',
'line_2',
@@ -35,10 +35,7 @@ class CachedAddress extends Model
'expanded_result',
];
- /**
- * @var array
- */
- public static $expandedFields = [
+ public static array $expandedFields = [
'thoroughfare',
'building_name',
'sub_building_name',
@@ -55,10 +52,7 @@ class CachedAddress extends Model
'country',
];
- /**
- * @var array
- */
- public static $fields = [
+ public static array $fields = [
'line_1',
'line_2',
'line_3',
@@ -68,9 +62,6 @@ class CachedAddress extends Model
'county',
];
- /**
- * @return string
- */
public function getFormattedStringAttribute(): string
{
return $this->toString(true);
@@ -80,15 +71,18 @@ public function getFormattedStringAttribute(): string
* Returns a string based on the address.
*
* @param bool $removeEmptyElements Prevents strings having conjoining commas
- *
- * @return string
*/
- public function toString($removeEmptyElements = false): string
+ public function toString(bool $removeEmptyElements = false): string
{
- if (!$removeEmptyElements) {
+ if ($removeEmptyElements === false) {
return implode(',', $this->only(self::$fields));
}
return implode(', ', array_filter($this->only(self::$fields)));
}
+
+ protected static function newFactory(): CachedAddressFactory
+ {
+ return new CachedAddressFactory;
+ }
}
diff --git a/src/Responses/AbstractWhitelist.php b/src/Responses/AbstractWhitelist.php
deleted file mode 100755
index 6a55539..0000000
--- a/src/Responses/AbstractWhitelist.php
+++ /dev/null
@@ -1,44 +0,0 @@
-id = $id;
- $this->name = $name;
- }
-
- /**
- * Get Object ID.
- *
- * @return string
- */
- public function getObjectId(): string
- {
- return $this->id;
- }
-}
diff --git a/src/Responses/Address.php b/src/Responses/Address.php
index 2e1790a..9c96864 100755
--- a/src/Responses/Address.php
+++ b/src/Responses/Address.php
@@ -4,139 +4,81 @@
class Address
{
- /**
- * Sort returned addresses numerically.
- *
- * @var bool
- */
- const SORT_NUMERICALLY = true;
-
- /**
- * Dont perform any specific sort on the returned addresses.
- *
- * @var bool
- */
- const NO_SORT = false;
-
- /**
- * Address string.
- *
- * @var array
- */
- protected $address = [];
+ protected array $address = [];
- /**
- * Constructor.
- *
- * @param string $address
- *
- * @return void
- */
- public function __construct($address)
+ public function __construct(string|array $address)
{
- $this->address = explode(',', $address);
+ $this->address = is_string($address) ? explode(',', $address) : $address;
}
- /**
- * Get Line 1.
- *
- * @return string
- */
public function getLine1(): string
{
- return $this->address[0];
+ return $this->address[0] ?? $this->address['line_1'];
}
- /**
- * Get Line 2.
- *
- * @return string
- */
public function getLine2(): string
{
- return $this->address[1];
+ return $this->address[1] ?? $this->address['line_2'];
}
- /**
- * Get Line 3.
- *
- * @return string
- */
public function getLine3(): string
{
- return $this->address[2];
+ return $this->address[2] ?? $this->address['line_3'];
}
- /**
- * Get Line 4.
- *
- * @return string
- */
public function getLine4(): string
{
- return $this->address[3];
+ return $this->address[3] ?? $this->address['line_4'];
}
- /**
- * Get Line.
- *
- * @param int $line
- *
- * @return string
- */
- public function getLine($line): string
+ public function getLine(int $line): ?string
{
- return $this->address[$line - 1];
+ if ($line > 4) {
+ return null;
+ }
+
+ return $this->address[$line - 1] ?? null;
}
- /**
- * Get Locality.
- *
- * @return string
- */
public function getLocality(): string
{
- return $this->address[4];
+ return $this->address[4] ?? $this->address['locality'];
}
- /**
- * Get Town.
- *
- * @return string
- */
public function getTown(): string
{
- return $this->address[5];
+ return $this->address[5] ?? $this->address['town_or_city'];
}
- /**
- * Get City.
- *
- * @return string
- *
- * @see Address:getTown()
- */
public function getCity(): string
{
- return $this->address[5];
+ return $this->address[5] ?? $this->address['town_or_city'];
}
- /**
- * County.
- *
- * @return string
- */
public function getCounty(): string
{
- return $this->address[6];
+ return $this->address[6] ?? $this->address['county'];
+ }
+
+ public function getDistrict(): ?string
+ {
+ return $this->address[7] ?? $this->address['district'] ?? null;
+ }
+
+ public function getCountry(): ?string
+ {
+ return $this->address[8] ?? $this->address['country'] ?? null;
+ }
+
+ public function isResidential(): bool
+ {
+ return $this->address[9] ?? $this->address['residential'];
}
/**
* Return a formatted array for the address.
*
* @param array $keys Override default key names
- *
- * @return array
*/
public function toArray(array $keys = []): array
{
@@ -152,37 +94,24 @@ public function toArray(array $keys = []): array
* Returns a string based on the address.
*
* @param bool $removeEmptyElements Prevents strings having conjoining commas
- *
- * @return string
*/
- public function toString($removeEmptyElements = false): string
+ public function toString(bool $removeEmptyElements = false): string
{
- if (!$removeEmptyElements) {
+ if ($removeEmptyElements === false) {
return implode(',', $this->address);
}
- return implode(',', array_filter($this->address, function ($value) {
- return trim($value) !== '';
- }));
+ return implode(',', array_filter(
+ $this->address,
+ static fn (?string $value): bool => filled($value)
+ ));
}
- /**
- * Compare two addresses to see if they are equal.
- *
- * @param \Laralabs\GetAddress\Responses\Address $address Address to compare
- *
- * @return bool
- */
public function sameAs(self $address): bool
{
return !array_diff($this->address, $address->toArray());
}
- /**
- * Convert the address to a comma separated string.
- *
- * @return string
- */
public function __toString(): string
{
return $this->toString();
diff --git a/src/Responses/AddressCollectionResponse.php b/src/Responses/AddressCollectionResponse.php
index 2169ad7..6afec27 100755
--- a/src/Responses/AddressCollectionResponse.php
+++ b/src/Responses/AddressCollectionResponse.php
@@ -6,117 +6,50 @@
class AddressCollectionResponse
{
- /**
- * @var string
- */
- protected $postcode;
-
- /**
- * Latitude.
- *
- * @var float
- */
- protected $latitude;
-
- /**
- * Longitude.
- *
- * @var float
- */
- protected $longitude;
-
- /**
- * Addresses.
- *
- * @var array
- */
- protected $addresses = [];
-
- /**
- * Constructor.
- *
- * @param string $postcode
- * @param float $latitude
- * @param float $longitude
- * @param array $addresses
- */
- public function __construct($postcode, $latitude, $longitude, array $addresses = [])
- {
- $this->postcode = $postcode;
- $this->latitude = $latitude;
- $this->longitude = $longitude;
- $this->addresses = $addresses;
+ public function __construct(
+ protected ?string $postcode,
+ protected ?float $latitude,
+ protected ?float $longitude,
+ protected array $addresses = []
+ ) {
}
- /**
- * Get Postcode.
- *
- * @return string
- */
- public function getPostcode(): string
+ public function getPostcode(): ?string
{
return $this->postcode;
}
- /**
- * Get Latitude.
- *
- * @return float
- */
- public function getLatitude(): float
+ public function getLatitude(): ?float
{
return $this->latitude;
}
- /**
- * Get Longitude.
- *
- * @return float
- */
- public function getLongitude(): float
+ public function getLongitude(): ?float
{
return $this->longitude;
}
- /**
- * Get Addresses.
- *
- * @return array
- */
public function getAddresses(): array
{
return $this->addresses;
}
- /**
- * Return response as array.
- *
- * @return array
- */
public function toArray(): array
{
return [
'postcode' => $this->postcode,
'latitude' => $this->latitude,
'longitude' => $this->longitude,
- 'addresses' => array_map(function ($address) {
+ 'addresses' => array_map(static function ($address): string|array {
if ($address instanceof Address) {
return $address->toString(true);
}
- if ($address instanceof ExpandedAddress) {
- return $address->toArray();
- }
- return $address;
+ return (array) $address;
}, $this->addresses),
];
}
- /**
- * Return a json response.
- *
- * @return JsonResponse
- */
public function respond(): JsonResponse
{
return response()->json($this->toArray());
diff --git a/src/Responses/AutocompleteCollectionResponse.php b/src/Responses/AutocompleteCollectionResponse.php
new file mode 100755
index 0000000..7a2543b
--- /dev/null
+++ b/src/Responses/AutocompleteCollectionResponse.php
@@ -0,0 +1,32 @@
+suggestions = $suggestions;
+ }
+
+ public function all(): ?array
+ {
+ return $this->suggestions;
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'suggestions' => $this->suggestions,
+ ];
+ }
+
+ public function respond(): JsonResponse
+ {
+ return response()->json($this->toArray());
+ }
+}
diff --git a/src/Responses/Domain.php b/src/Responses/Domain.php
deleted file mode 100755
index 3edd5c5..0000000
--- a/src/Responses/Domain.php
+++ /dev/null
@@ -1,16 +0,0 @@
-name;
- }
-}
diff --git a/src/Responses/ExpandedAddress.php b/src/Responses/ExpandedAddress.php
index ed3c8b2..0fa5ada 100755
--- a/src/Responses/ExpandedAddress.php
+++ b/src/Responses/ExpandedAddress.php
@@ -2,186 +2,93 @@
namespace Laralabs\GetAddress\Responses;
-class ExpandedAddress
-{
- /**
- * Address string.
- *
- * @var array
- */
- protected $address = [];
+use Illuminate\Contracts\Support\Arrayable;
- /**
- * Constructor.
- *
- * @param string $address
- *
- * @return void
- */
- public function __construct($address)
+class ExpandedAddress implements Arrayable
+{
+ public function __construct(protected array $address = [])
{
- $this->address = $address;
}
- /**
- * Get Thoroughfare.
- *
- * @return string
- */
public function getThoroughfare(): string
{
return $this->address['thoroughfare'];
}
- /**
- * Get Building Name.
- *
- * @return string
- */
public function getBuildingName(): string
{
return $this->address['building_name'];
}
- /**
- * Get Sub Building Name.
- *
- * @return string
- */
public function getSubBuildingName(): string
{
return $this->address['sub_building_name'];
}
- /**
- * Get Building Number.
- *
- * @return string
- */
public function getBuildingNumber(): string
{
return $this->address['building_number'];
}
- /**
- * Get Sub Building Number.
- *
- * @return string
- */
public function getSubBuildingNumber(): string
{
return $this->address['sub_building_number'];
}
- /**
- * Get Line 1.
- *
- * @return string
- */
public function getLine1(): string
{
return $this->address['line_1'];
}
- /**
- * Get Line 2.
- *
- * @return string
- */
public function getLine2(): string
{
return $this->address['line_2'];
}
- /**
- * Get Line 3.
- *
- * @return string
- */
public function getLine3(): string
{
return $this->address['line_3'];
}
- /**
- * Get Line 4.
- *
- * @return string
- */
public function getLine4(): string
{
return $this->address['line_4'];
}
- /**
- * Get Line.
- *
- * @param int $line
- *
- * @return string
- */
- public function getLine($line): string
+ public function getLine(int $line): ?string
{
- return $this->address['line_'.$line];
+ if ($line > 4) {
+ return null;
+ }
+
+ return $this->address['line_' . $line];
}
- /**
- * Get Locality.
- *
- * @return string
- */
public function getLocality(): string
{
return $this->address['locality'];
}
- /**
- * Get Town.
- *
- * @return string
- */
public function getTown(): string
{
return $this->address['town_or_city'];
}
- /**
- * Get City.
- *
- * @return string
- *
- * @see ExpandedAddress:getTown()
- */
public function getCity(): string
{
return $this->address['town_or_city'];
}
- /**
- * Get County.
- *
- * @return string
- */
public function getCounty(): string
{
return $this->address['county'];
}
- /**
- * Get District.
- *
- * @return string
- */
public function getDistrict(): string
{
return $this->address['district'];
}
- /**
- * Get Country.
- *
- * @return string
- */
public function getCountry(): string
{
return $this->address['country'];
@@ -189,51 +96,28 @@ public function getCountry(): string
/**
* Return a formatted array for the address.
- *
- * @param array $keys Override default key names
- *
- * @return array
*/
- public function toArray(array $keys = []): array
+ public function toArray(): array
{
return array_merge([
'formatted_string' => $this->toString(true),
], $this->address);
}
- /**
- * Returns a string based on the address.
- *
- * @param bool $removeEmptyElements Prevents strings having conjoining commas
- *
- * @return string
- */
- public function toString($removeEmptyElements = false): string
+ public function toString(bool $removeEmptyElements = false): string
{
- if (!$removeEmptyElements) {
- return implode(',', $this->address['formatted_address']);
- }
+ $separator = isset($this->address['formatted_address']) ? ', ' : ',';
- return implode(', ', array_filter($this->address['formatted_address']));
- }
+ if ($removeEmptyElements === false) {
+ return implode($separator, $this->address['formatted_address']);
+ }
- /**
- * Compare two addresses to see if they are equal.
- *
- * @param \Laralabs\GetAddress\Responses\Address $address Address to compare
- *
- * @return bool
- */
- public function sameAs(Address $address): bool
- {
- return !array_diff($this->address, $address->toArray());
+ return implode($separator, array_filter(
+ $this->address['formatted_address'] ?? $this->address,
+ static fn (?string $value): bool => filled($value)
+ ));
}
- /**
- * Convert the address to a comma separated string.
- *
- * @return string
- */
public function __toString(): string
{
return $this->toString();
diff --git a/src/Responses/Ip.php b/src/Responses/Ip.php
deleted file mode 100755
index 27adde0..0000000
--- a/src/Responses/Ip.php
+++ /dev/null
@@ -1,16 +0,0 @@
-name;
- }
-}
diff --git a/src/Responses/PrivateAddress.php b/src/Responses/PrivateAddress.php
deleted file mode 100755
index 9558d02..0000000
--- a/src/Responses/PrivateAddress.php
+++ /dev/null
@@ -1,75 +0,0 @@
-privateAddressId = $addressId;
- }
-
- /**
- * Create a new private address object ready to submit to the API.
- *
- * @param string $line1
- * @param string $line2
- * @param string $line3
- * @param string $line4
- * @param string $locality
- * @param string $townOrCity
- * @param string $county
- *
- * @return \Laralabs\GetAddress\Responses\PrivateAddress
- */
- public static function create(
- $line1 = '',
- $line2 = '',
- $line3 = '',
- $line4 = '',
- $locality = '',
- $townOrCity = '',
- $county = ''
- ) {
- $address = implode(',', func_get_args());
-
- return new static(null, $address);
- }
-
- /**
- * Get Address ID.
- *
- * @return string
- */
- public function getAddressId(): string
- {
- return $this->privateAddressId;
- }
-
- /**
- * Is Saved.
- *
- * @return bool
- */
- public function isSaved(): bool
- {
- return $this->privateAddressId !== null;
- }
-}
diff --git a/src/Responses/PrivateAddressResponse.php b/src/Responses/PrivateAddressResponse.php
deleted file mode 100755
index c3410e5..0000000
--- a/src/Responses/PrivateAddressResponse.php
+++ /dev/null
@@ -1,37 +0,0 @@
-message = $message;
- }
-
- /**
- * Get Message.
- *
- * @return string
- */
- public function getMessage(): string
- {
- return $this->message;
- }
-}
diff --git a/src/Responses/SingleAddressCollectionResponse.php b/src/Responses/SingleAddressCollectionResponse.php
new file mode 100755
index 0000000..67366c7
--- /dev/null
+++ b/src/Responses/SingleAddressCollectionResponse.php
@@ -0,0 +1,42 @@
+postcode = $address['postcode'] ?? null;
+ $this->latitude = $address['latitude'] ?? null;
+ $this->longitude = $address['longitude'] ?? null;
+ $this->address = $address;
+ $this->expand = $expand;
+
+ parent::__construct($this->postcode, $this->latitude, $this->longitude, [$this->address]);
+ }
+
+ public function getAddress(): ExpandedAddress|Address
+ {
+ return $this->expand ? new ExpandedAddress($this->address) : new Address($this->address);
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'postcode' => $this->postcode,
+ 'latitude' => $this->latitude,
+ 'longitude' => $this->longitude,
+ 'address' => $this->address,
+ ];
+ }
+}
diff --git a/src/Responses/Usage.php b/src/Responses/Usage.php
deleted file mode 100755
index 4618378..0000000
--- a/src/Responses/Usage.php
+++ /dev/null
@@ -1,135 +0,0 @@
-count = (int) $count;
- $this->limits = [
- (int) $limit1,
- (int) $limit2,
- ];
- }
-
- /**
- * Get Count.
- *
- * @return int
- */
- public function getCount(): int
- {
- return $this->count;
- }
-
- /**
- * Get Limit 1.
- *
- * @return int
- */
- public function getLimit1(): int
- {
- return $this->limits[0];
- }
-
- /**
- * Get Limit 2.
- *
- * @return int
- */
- public function getLimit2(): int
- {
- return $this->limits[1];
- }
-
- /**
- * Get Limit.
- *
- * @param int $limitNumber
- *
- * @return int
- */
- public function getLimit($limitNumber): int
- {
- return $this->limits[$limitNumber - 1];
- }
-
- /**
- * Get Limits.
- *
- * @return array
- */
- public function getLimits(): array
- {
- return $this->limits;
- }
-
- /**
- * Requests Remaining.
- *
- * @param bool $untilSlowed Will return requests remaining until calls are slowed by getAddress
- *
- * @return int
- */
- public function requestsRemaining($untilSlowed = false): int
- {
- $limit = $untilSlowed ? $this->limits[0] : $this->limits[1];
-
- return $limit - $this->count;
- }
-
- /**
- * Requests Remaining Until Slowed.
- *
- * @return int
- */
- public function requestsRemainingUntilSlowed(): int
- {
- return $this->requestsRemaining(true);
- }
-
- /**
- * Has Exceeded Limit.
- *
- * @return bool
- */
- public function hasExceededLimit(): bool
- {
- return $this->count > $this->limits[1];
- }
-
- /**
- * Returns whether the initial limit has been reached and whether subsequent
- * requests have been slowed down by getAddress.
- *
- * @return bool
- */
- public function isRestricted(): bool
- {
- return $this->count >= $this->limits[0];
- }
-}
diff --git a/src/Responses/Webhook.php b/src/Responses/Webhook.php
deleted file mode 100755
index 061f63b..0000000
--- a/src/Responses/Webhook.php
+++ /dev/null
@@ -1,54 +0,0 @@
-id = $id;
- $this->url = $url;
- }
-
- /**
- * Get Webhook ID.
- *
- * @return string
- */
- public function getWebhookId(): string
- {
- return $this->id;
- }
-
- /**
- * Get Webhook Url.
- *
- * @return string
- */
- public function getWebhookUrl(): string
- {
- return $this->url;
- }
-}
diff --git a/src/Responses/WebhookResponse.php b/src/Responses/WebhookResponse.php
deleted file mode 100755
index 552a6b1..0000000
--- a/src/Responses/WebhookResponse.php
+++ /dev/null
@@ -1,52 +0,0 @@
-message = $message;
- $this->hooks = $hooks;
- }
-
- /**
- * Get Message.
- *
- * @return string
- */
- public function getMessage(): string
- {
- return $this->message;
- }
-
- /**
- * Get Hooks.
- *
- * @return array
- */
- public function getHooks(): array
- {
- return $this->hooks;
- }
-}
diff --git a/src/Responses/WhitelistResponse.php b/src/Responses/WhitelistResponse.php
deleted file mode 100755
index f3d125a..0000000
--- a/src/Responses/WhitelistResponse.php
+++ /dev/null
@@ -1,52 +0,0 @@
-message = $message;
- $this->items = $items;
- }
-
- /**
- * Get Message.
- *
- * @return string
- */
- public function getMessage(): string
- {
- return $this->message;
- }
-
- /**
- * Get Items.
- *
- * @return array
- */
- public function getItems(): array
- {
- return $this->items;
- }
-}
diff --git a/tests/Feature/GetAddressTest.php b/tests/Feature/GetAddressTest.php
new file mode 100644
index 0000000..985805e
--- /dev/null
+++ b/tests/Feature/GetAddressTest.php
@@ -0,0 +1,197 @@
+getHttpFake();
+
+ $results = GetAddress::find('B13 9SZ');
+
+ $this->assertCount(14, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_do_a_postcode_lookup_with_property_number_using_find(): void
+ {
+ ResponseFactory::make('singleFindResponse.json')->getHttpFake();
+
+ $results = get_address()->find('B13 9SZ', 32);
+
+ $this->assertCount(1, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_do_a_postcode_lookup_with_property_number_using_find_and_results_from_the_cache(): void
+ {
+ config()->set('getaddress.enable_cache', true);
+
+ $manager = new Manager();
+ $manager->responseToCache(
+ ResponseFactory::make('successfulFindResponse.json')->makeAddressCollectionResponse()
+ );
+
+ $this->assertEquals(14, CachedAddress::count());
+
+ $results = GetAddress::find('B13 9SZ');
+
+ $this->assertCount(14, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_do_a_postcode_lookup_using_find_and_results_from_the_cache(): void
+ {
+ config()->set('getaddress.enable_cache', true);
+
+ $manager = new Manager();
+ $manager->responseToCache(
+ ResponseFactory::make('successfulFindResponse.json')->makeAddressCollectionResponse()
+ );
+
+ $this->assertEquals(14, CachedAddress::count());
+
+ $results = get_address()->find('B13 9SZ', 32);
+
+ $this->assertCount(2, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_do_a_postcode_lookup_using_find_then_store_and_return_results_from_the_cache(): void
+ {
+ Carbon::setTestNow('2024-02-14 12:00:00');
+
+ config()->set('getaddress.enable_cache', true);
+
+ ResponseFactory::make('successfulFindResponse.json')->getHttpFake();
+
+ CachedAddress::factory()->create(['line_1' => '1 Example Street', 'postcode' => 'B13 9SZ']);
+
+ $this->assertEquals(1, CachedAddress::count());
+
+ Carbon::setTestNow('2024-03-15 12:00:00');
+
+ $results = get_address()->expand()->find('B13 9SZ');
+
+ $this->assertEquals(14, CachedAddress::count());
+
+ $this->assertCount(14, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_do_a_postcode_lookup_using_find_and_store_response_in_cache(): void
+ {
+ config()->set('getaddress.enable_cache', true);
+
+ ResponseFactory::make('successfulFindResponse.json')->getHttpFake();
+
+ $this->assertEquals(0, CachedAddress::count());
+
+ $results = get_address()->find('B13 9SZ');
+
+ $this->assertEquals(14, CachedAddress::count());
+
+ $this->assertCount(14, $results->getAddresses());
+ $this->assertEquals('B13 9SZ', $results->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_perform_an_autocomplete_request(): void
+ {
+ ResponseFactory::make('successfulAutocompleteResponse.json')->getHttpFake();
+
+ $results = GetAddress::autocomplete('32 Clarence');
+
+ $this->assertCount(6, $results->all());
+ }
+
+ /** @test */
+ public function it_can_perform_an_autocomplete_request_and_respond_with_json_response(): void
+ {
+ ResponseFactory::make('successfulAutocompleteResponse.json')->getHttpFake();
+
+ $results = GetAddress::autocomplete('32 Clarence');
+
+ $this->assertCount(6, $results->respond()->getOriginalContent()['suggestions']);
+ }
+
+ /** @test */
+ public function it_can_perform_a_get_request_for_an_autocomplete_result_item(): void
+ {
+ config()->set('getaddress.expanded_results', true);
+
+ ResponseFactory::make('successfulGetResponse.json')->getHttpFake();
+
+ $results = GetAddress::get('NmNhMTg3ZjBkZmQ1OTg0IDEwMzgwMzg1IDdjZmFmNTA5OTI3YjkzZQ==');
+
+ $this->assertEquals('SK10 5GR', $results->getPostcode());
+ $this->assertEquals(53.2998, $results->getLatitude());
+ $this->assertEquals(-2.102, $results->getLongitude());
+ $this->assertCount(1, $results->getAddresses());
+ $this->assertMatchesJsonSnapshot($results->toArray());
+ }
+
+ /** @test */
+ public function it_can_handle_a_429_error_and_reattempt_on_find(): void
+ {
+ ResponseFactory::make('successfulFindResponse.json')->getHttpErrorFake(429);
+
+ $results = get_address()->find('B13 9SZ');
+
+ $this->assertCount(14, $results->getAddresses());
+ }
+
+ /** @test */
+ public function it_can_handle_a_429_error_and_reattempt_on_autocomplete(): void
+ {
+ ResponseFactory::make('successfulAutocompleteResponse.json')->getHttpErrorFake(429);
+
+ $results = GetAddress::autocomplete('32 Clarence');
+
+ $this->assertCount(6, $results->all());
+ }
+
+ /** @test */
+ public function it_can_handle_a_429_error_and_reattempt_on_get(): void
+ {
+ ResponseFactory::make('successfulGetResponse.json')->getHttpErrorFake(429);
+
+ $results = GetAddress::get('NmNhMTg3ZjBkZmQ1OTg0IDEwMzgwMzg1IDdjZmFmNTA5OTI3YjkzZQ==');
+
+ $this->assertCount(1, $results->getAddresses());
+ }
+
+ /** @test */
+ public function it_can_handle_a_server_error_and_throw_an_exception(): void
+ {
+ ResponseFactory::make('successfulGetResponse.json')->getHttpErrorFake(500);
+
+ try {
+ GetAddress::get('NmNhMTg3ZjBkZmQ1OTg0IDEwMzgwMzg1IDdjZmFmNTA5OTI3YjkzZQ==');
+ } catch (ServerError $exception) {
+ $this->assertEquals('getAddress.io is currently having issues.', $exception->getMessage());
+ return;
+ }
+
+ $this->fail('ServerError exception not thrown');
+ }
+}
diff --git a/tests/Feature/__snapshots__/GetAddressTest__it_can_perform_a_get_request_for_an_autocomplete_result_item__1.json b/tests/Feature/__snapshots__/GetAddressTest__it_can_perform_a_get_request_for_an_autocomplete_result_item__1.json
new file mode 100644
index 0000000..325d01f
--- /dev/null
+++ b/tests/Feature/__snapshots__/GetAddressTest__it_can_perform_a_get_request_for_an_autocomplete_result_item__1.json
@@ -0,0 +1,32 @@
+{
+ "postcode": "SK10 5GR",
+ "latitude": 53.2998,
+ "longitude": -2.102,
+ "address": {
+ "postcode": "SK10 5GR",
+ "latitude": 53.2998,
+ "longitude": -2.102,
+ "formatted_address": [
+ "Apartment 32",
+ "Clarence Mill",
+ "Clarence Road",
+ "Bollington, Macclesfield",
+ "Cheshire"
+ ],
+ "thoroughfare": "Clarence Road",
+ "building_name": "Clarence Mill",
+ "sub_building_name": "",
+ "sub_building_number": "32",
+ "building_number": "",
+ "line_1": "Apartment 32",
+ "line_2": "Clarence Mill",
+ "line_3": "Clarence Road",
+ "line_4": "",
+ "locality": "Bollington",
+ "town_or_city": "Macclesfield",
+ "county": "Cheshire",
+ "district": "Cheshire East",
+ "country": "England",
+ "residential": true
+ }
+}
diff --git a/tests/Support/Responses/ResponseFactory.php b/tests/Support/Responses/ResponseFactory.php
new file mode 100644
index 0000000..d62759b
--- /dev/null
+++ b/tests/Support/Responses/ResponseFactory.php
@@ -0,0 +1,94 @@
+fileName = $fileName;
+ $this->response = file_get_contents(__DIR__ . '/examples/' . $fileName);
+ $this->decodedResponse = json_decode($this->response, true);
+ $this->expand = $expand;
+ }
+
+ public static function make(string $fileName, bool $expand = false): self
+ {
+ return new self($fileName, $expand);
+ }
+
+ public function getHttpFake(?array $attributes = null): Factory
+ {
+ return Http::fake($attributes ?? [
+ '*' => Http::response(ResponseFactory::make($this->fileName)->getResponse()),
+ ]);
+ }
+
+ public function getHttpErrorFake(int $code, mixed $content = []): Factory
+ {
+ return Http::fake([
+ '*' => function () use ($code, $content): PromiseInterface {
+ static $callCount = 0;
+
+ $callCount += 1;
+
+ if ($callCount === 1) {
+ return Http::response($content, $code);
+ }
+
+ return Http::response(ResponseFactory::make($this->fileName)->getResponse());
+ },
+ ]);
+ }
+
+ public function getResponse(): string
+ {
+ return $this->response;
+ }
+
+ public function getDecodedResponse(): array
+ {
+ return $this->decodedResponse;
+ }
+
+ public function makeAddressCollectionResponse(): AddressCollectionResponse
+ {
+ return new AddressCollectionResponse(
+ Arr::get($this->decodedResponse, 'postcode'),
+ Arr::get($this->decodedResponse, 'latitude'),
+ Arr::get($this->decodedResponse, 'longitude'),
+ $this->hydrateAddresses()
+ );
+ }
+
+ public function makeSingleAddressCollectionResponse(): SingleAddressCollectionResponse
+ {
+ return new SingleAddressCollectionResponse($this->decodedResponse, $this->expand);
+ }
+
+ private function hydrateAddresses(): array
+ {
+ return collect($this->decodedResponse['addresses'])->transform(
+ fn (array $address): ExpandedAddress|Address => $this->expand ? new ExpandedAddress(
+ $address
+ ) : new Address(array_values($address))
+ )->toArray();
+ }
+}
diff --git a/tests/Support/Responses/examples/singleFindResponse.json b/tests/Support/Responses/examples/singleFindResponse.json
new file mode 100644
index 0000000..586db68
--- /dev/null
+++ b/tests/Support/Responses/examples/singleFindResponse.json
@@ -0,0 +1 @@
+{"postcode":"B13 9SZ","latitude":52.437353914285715,"longitude":-1.8828399142857144,"addresses":[{"line_1":"32 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"}]}
\ No newline at end of file
diff --git a/tests/Support/Responses/examples/successfulAutocompleteResponse.json b/tests/Support/Responses/examples/successfulAutocompleteResponse.json
new file mode 100644
index 0000000..1882001
--- /dev/null
+++ b/tests/Support/Responses/examples/successfulAutocompleteResponse.json
@@ -0,0 +1 @@
+{"suggestions":[{"address":"Apartment 32, Clarence Mill, Clarence Road, Bollington, Macclesfield","url":"\/get\/NmNhMTg3ZjBkZmQ1OTg0IDEwMzgwMzg1IDdjZmFmNTA5OTI3YjkzZQ==","id":"NmNhMTg3ZjBkZmQ1OTg0IDEwMzgwMzg1IDdjZmFmNTA5OTI3YjkzZQ=="},{"address":"Revive Digital Solutions Ltd, Clarence Street Chambers, 32 Clarence Street, Southend-on-Sea","url":"\/get\/MmY4NmNmODQ3ODE1NWE2IDIzNDEyMDgwNTcgN2NmYWY1MDk5MjdiOTNl","id":"MmY4NmNmODQ3ODE1NWE2IDIzNDEyMDgwNTcgN2NmYWY1MDk5MjdiOTNl"},{"address":"Looking Good Consultants Ltd, Clarence Street Chambers, 32 Clarence Street, Southend-on-Sea","url":"\/get\/NDM3YjJiYzk2NGE0M2RjIDIzNDA3Nzc3NjggN2NmYWY1MDk5MjdiOTNl","id":"NDM3YjJiYzk2NGE0M2RjIDIzNDA3Nzc3NjggN2NmYWY1MDk5MjdiOTNl"},{"address":"Maven Fm Services Ltd, Clarence Street Chambers, 32 Clarence Street, Southend-on-Sea","url":"\/get\/YmVkYjY1MDU4ODYwY2I5IDIzNDA4MTM1NjAgN2NmYWY1MDk5MjdiOTNl","id":"YmVkYjY1MDU4ODYwY2I5IDIzNDA4MTM1NjAgN2NmYWY1MDk5MjdiOTNl"},{"address":"Avamore Finance 1 Ltd, Clarence Street Chambers, 32 Clarence Street, Southend-on-Sea","url":"\/get\/NGE1ZmY1YTMwYTBlM2JhIDIzNDA4NTc2NjcgN2NmYWY1MDk5MjdiOTNl","id":"NGE1ZmY1YTMwYTBlM2JhIDIzNDA4NTc2NjcgN2NmYWY1MDk5MjdiOTNl"},{"address":"Hyde Built Homes Ltd, Clarence Street Chambers, 32 Clarence Street, Southend-on-Sea","url":"\/get\/ZDZlZDA0YzA5MjNiM2RjIDIzNDEwNzE2MzMgN2NmYWY1MDk5MjdiOTNl","id":"ZDZlZDA0YzA5MjNiM2RjIDIzNDEwNzE2MzMgN2NmYWY1MDk5MjdiOTNl"}]}
\ No newline at end of file
diff --git a/tests/Support/Responses/examples/successfulFindResponse.json b/tests/Support/Responses/examples/successfulFindResponse.json
new file mode 100644
index 0000000..19e2c3b
--- /dev/null
+++ b/tests/Support/Responses/examples/successfulFindResponse.json
@@ -0,0 +1 @@
+{"postcode":"B13 9SZ","latitude":52.437353914285715,"longitude":-1.8828399142857144,"addresses":[{"line_1":"32 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"32a Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"34 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"36 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"38 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"40 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"41 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"43 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"45 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"47 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"49 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"51 Clarence Road","line_2":" ","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"Flat 1","line_2":" 36 Clarence Road","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"},{"line_1":"Flat 2","line_2":" 36 Clarence Road","line_3":" ","line_4":" ","locality":" Moseley","town_or_city":" Birmingham","county":" West Midlands"}]}
\ No newline at end of file
diff --git a/tests/Support/Responses/examples/successfulGetResponse.json b/tests/Support/Responses/examples/successfulGetResponse.json
new file mode 100644
index 0000000..9367886
--- /dev/null
+++ b/tests/Support/Responses/examples/successfulGetResponse.json
@@ -0,0 +1 @@
+{"postcode":"SK10 5GR","latitude":53.2998,"longitude":-2.102,"formatted_address":["Apartment 32","Clarence Mill","Clarence Road","Bollington, Macclesfield","Cheshire"],"thoroughfare":"Clarence Road","building_name":"Clarence Mill","sub_building_name":"","sub_building_number":"32","building_number":"","line_1":"Apartment 32","line_2":"Clarence Mill","line_3":"Clarence Road","line_4":"","locality":"Bollington","town_or_city":"Macclesfield","county":"Cheshire","district":"Cheshire East","country":"England","residential":true}
\ No newline at end of file
diff --git a/tests/TestCase.php b/tests/TestCase.php
index f171d8f..87fb822 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -6,12 +6,7 @@
abstract class TestCase extends \Orchestra\Testbench\TestCase
{
- public function setUp()
- {
- parent::setUp();
- }
-
- protected function getPackageProviders($app)
+ protected function getPackageProviders($app): array //phpcs:ignore
{
return [
GetAddressServiceProvider::class,
diff --git a/tests/Unit/Cache/ManagerTest.php b/tests/Unit/Cache/ManagerTest.php
new file mode 100644
index 0000000..bdcf478
--- /dev/null
+++ b/tests/Unit/Cache/ManagerTest.php
@@ -0,0 +1,87 @@
+manager = new Manager();
+ }
+
+ /** @test */
+ public function it_can_check_for_cached_addresses_with_postcode(): void
+ {
+ CachedAddress::factory()->create(['line_1' => '1 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '2 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '3 Example Street', 'postcode' => 'ABC 321']);
+
+ $result = $this->manager->checkCache('ABC 123', null);
+
+ $this->assertCount(2, $result['addresses'] ?? 0);
+ }
+
+ /** @test */
+ public function it_can_check_for_expanded_cached_addresses_with_postcode(): void
+ {
+ CachedAddress::factory()->create(['line_1' => '1 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '2 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '3 Example Street', 'postcode' => 'ABC 321']);
+
+ $result = $this->manager->expand()->checkCache('ABC 123', null);
+
+ $this->assertCount(2, $result['addresses'] ?? 0);
+ }
+
+ /** @test */
+ public function it_can_check_for_cached_addresses_with_postcode_and_property_number(): void
+ {
+ CachedAddress::factory()->create(['line_1' => '1 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '2 Example Street', 'postcode' => 'ABC 123']);
+ CachedAddress::factory()->create(['line_1' => '3 Example Street', 'postcode' => 'ABC 321']);
+
+ $result = $this->manager->checkCache('ABC 123', 1);
+
+ $this->assertCount(1, $result['addresses'] ?? 0);
+
+ $this->assertEquals('1 Example Street, Moseley, Birmingham, West Midlands', $result['addresses'][0]);
+ $this->assertEquals('ABC 123', $result['postcode']);
+ }
+
+ /** @test */
+ public function it_can_store_the_address_collection_response_to_cache(): void
+ {
+ $response = ResponseFactory::make('successfulFindResponse.json')
+ ->makeAddressCollectionResponse();
+ $expectedAddress = $response->getAddresses()[0];
+
+ $this->assertEquals(0, CachedAddress::count());
+
+ $this->manager->responseToCache($response);
+
+ $this->assertEquals(14, CachedAddress::count());
+ $this->assertEquals($expectedAddress->getLine1(), CachedAddress::first()->line_1);
+ $this->assertEquals($expectedAddress->getLine2(), CachedAddress::first()->line_2);
+ $this->assertEquals($expectedAddress->getLine3(), CachedAddress::first()->line_3);
+ $this->assertEquals($expectedAddress->getLine4(), CachedAddress::first()->line_4);
+ $this->assertEquals($expectedAddress->getLocality(), CachedAddress::first()->locality);
+ $this->assertEquals($expectedAddress->getTown(), CachedAddress::first()->town_or_city);
+ $this->assertEquals($expectedAddress->getCity(), CachedAddress::first()->town_or_city);
+ $this->assertEquals($expectedAddress->getCounty(), CachedAddress::first()->county);
+ $this->assertEquals($response->getPostcode(), CachedAddress::first()->postcode);
+ $this->assertIsFloat(CachedAddress::first()->longitude);
+ $this->assertIsFloat(CachedAddress::first()->latitude);
+ }
+}
diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php
new file mode 100644
index 0000000..dd07e4f
--- /dev/null
+++ b/tests/Unit/Exceptions/HandlerTest.php
@@ -0,0 +1,99 @@
+assertEquals('getAddress responded with a 418 http status', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('Unknown exception not thrown');
+ }
+
+ /** @test */
+ public function it_throws_an_invalid_postcode_exception(): void
+ {
+ try {
+ Handler::throwException(400);
+ } catch (InvalidPostcode $exception) {
+ $this->assertEquals('The postcode you provided was not a valid format.', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('InvalidPostcode exception not thrown');
+ }
+
+ /** @test */
+ public function it_throws_a_forbidden_exception(): void
+ {
+ try {
+ Handler::throwException(401);
+ } catch (Forbidden $exception) {
+ $this->assertEquals('Your API key is not valid for this request.', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('Forbidden exception not thrown');
+ }
+
+ /** @test */
+ public function it_throws_a_postcode_not_found_exception(): void
+ {
+ try {
+ Handler::throwException(404);
+ } catch (PostcodeNotFound $exception) {
+ $this->assertEquals('The postcode you provided could not be found.', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('PostcodeNotFound exception not thrown');
+ }
+
+ /** @test */
+ public function it_throws_a_too_many_requests_exception(): void
+ {
+ try {
+ Handler::throwException(429);
+ } catch (TooManyRequests $exception) {
+ $this->assertEquals('You have made too many requests for this key.', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('TooManyRequests exception not thrown');
+ }
+
+ /** @test */
+ public function it_throws_a_server_exception(): void
+ {
+ try {
+ Handler::throwException(500);
+ } catch (ServerError $exception) {
+ $this->assertEquals('getAddress.io is currently having issues.', $exception->getMessage());
+
+ return;
+ }
+
+ $this->fail('ServerError exception not thrown');
+ }
+}
diff --git a/tests/Unit/Http/ClientTest.php b/tests/Unit/Http/ClientTest.php
new file mode 100644
index 0000000..002cee7
--- /dev/null
+++ b/tests/Unit/Http/ClientTest.php
@@ -0,0 +1,21 @@
+assertFalse($client->isAdminMode());
+
+ $client->admin();
+
+ $this->assertTrue($client->isAdminMode());
+ }
+}
diff --git a/tests/Unit/Models/CachedAddressTest.php b/tests/Unit/Models/CachedAddressTest.php
new file mode 100644
index 0000000..db98e14
--- /dev/null
+++ b/tests/Unit/Models/CachedAddressTest.php
@@ -0,0 +1,34 @@
+create();
+
+ $this->assertEquals(
+ '123 Example Street, Moseley, Birmingham, West Midlands',
+ $cachedAddress->getFormattedStringAttribute()
+ );
+ }
+
+ /** @test */
+ public function it_can_convert_the_cached_address_to_string_without_stripping_empty_elements(): void
+ {
+ $cachedAddress = CachedAddress::factory()->create();
+
+ $this->assertEquals(
+ '123 Example Street,,,,Moseley,Birmingham,West Midlands',
+ $cachedAddress->toString()
+ );
+ }
+}
diff --git a/tests/Unit/Responses/AddressCollectionResponseTest.php b/tests/Unit/Responses/AddressCollectionResponseTest.php
new file mode 100644
index 0000000..77e39d5
--- /dev/null
+++ b/tests/Unit/Responses/AddressCollectionResponseTest.php
@@ -0,0 +1,89 @@
+response = ResponseFactory::make('successfulFindResponse.json')
+ ->makeAddressCollectionResponse();
+ }
+
+ /** @test */
+ public function it_can_get_the_postcode(): void
+ {
+ $this->assertEquals('B13 9SZ', $this->response->getPostcode());
+ }
+
+ /** @test */
+ public function it_can_get_the_latitude(): void
+ {
+ $this->assertEquals(52.437353914285715, $this->response->getLatitude());
+ }
+
+ /** @test */
+ public function it_can_get_the_longitude(): void
+ {
+ $this->assertEquals(-1.8828399142857144, $this->response->getLongitude());
+ }
+
+ /** @test */
+ public function it_can_get_the_addresses(): void
+ {
+ $addresses = collect($this->response->getAddresses());
+
+ $this->assertCount(14, $addresses);
+ $this->assertInstanceOf(Address::class, $addresses->first());
+ }
+
+ /** @test */
+ public function it_can_transform_to_the_expected_array(): void
+ {
+ $this->assertMatchesJsonSnapshot($this->response->toArray());
+ }
+
+ /** @test */
+ public function it_can_transform_to_the_expected_array_with_expanded_addresses(): void
+ {
+ $response = ResponseFactory::make('successfulFindResponse.json', true)
+ ->makeAddressCollectionResponse();
+
+ $this->assertMatchesJsonSnapshot($response->toArray());
+ }
+
+ /** @test */
+ public function it_can_get_a_json_response(): void
+ {
+ $this->assertInstanceOf(JsonResponse::class, $this->response->respond());
+ }
+
+ /** @test */
+ public function it_can_instantiate_a_new_address_collection_response(): void
+ {
+ $response = new AddressCollectionResponse(
+ 'B13 9SZ',
+ 52.437353914285715,
+ -1.8828399142857144,
+ []
+ );
+
+ $this->assertEquals('B13 9SZ', $response->getPostcode());
+ $this->assertEquals(52.437353914285715, $response->getLatitude());
+ $this->assertEquals(-1.8828399142857144, $response->getLongitude());
+ $this->assertCount(0, $response->getAddresses());
+ }
+}
diff --git a/tests/Unit/Responses/AddressTest.php b/tests/Unit/Responses/AddressTest.php
new file mode 100644
index 0000000..d17de1e
--- /dev/null
+++ b/tests/Unit/Responses/AddressTest.php
@@ -0,0 +1,123 @@
+address = ResponseFactory::make('singleFindResponse.json')
+ ->makeAddressCollectionResponse()->getAddresses()[0];
+ }
+
+ /** @test */
+ public function it_can_get_line_one(): void
+ {
+ $this->assertEquals('32 Clarence Road', $this->address->getLine1());
+ }
+
+ /** @test */
+ public function it_can_get_line_two(): void
+ {
+ $this->assertEquals(' ', $this->address->getLine2());
+ }
+
+ /** @test */
+ public function it_can_get_line_three(): void
+ {
+ $this->assertEquals(' ', $this->address->getLine3());
+ }
+
+ /** @test */
+ public function it_can_get_line_four(): void
+ {
+ $this->assertEquals(' ', $this->address->getLine4());
+ }
+
+ /** @test */
+ public function it_can_get_a_line_by_the_given_number(): void
+ {
+ $this->assertEquals('32 Clarence Road', $this->address->getLine(1));
+ $this->assertNull($this->address->getLine(5));
+ }
+
+ /** @test */
+ public function it_can_get_the_locality(): void
+ {
+ $this->assertEquals(' Moseley', $this->address->getLocality());
+ }
+
+ /** @test */
+ public function it_can_get_the_town(): void
+ {
+ $this->assertEquals(' Birmingham', $this->address->getTown());
+ }
+
+ /** @test */
+ public function it_can_get_the_city(): void
+ {
+ $this->assertEquals(' Birmingham', $this->address->getCity());
+ }
+
+ /** @test */
+ public function it_can_get_the_county(): void
+ {
+ $this->assertEquals(' West Midlands', $this->address->getCounty());
+ }
+
+ /** @test */
+ public function it_can_get_the_district(): void
+ {
+ $this->assertNull($this->address->getDistrict());
+ }
+
+ /** @test */
+ public function it_can_get_the_country(): void
+ {
+ $this->assertNull($this->address->getCountry());
+ }
+
+ /** @test */
+ public function it_can_transform_address_to_string_and_remove_empty_elements(): void
+ {
+ $this->assertEquals(
+ '32 Clarence Road, Moseley, Birmingham, West Midlands',
+ $this->address->toString(true)
+ );
+ }
+
+ /** @test */
+ public function it_can_transform_address_to_string(): void
+ {
+ $this->assertEquals(
+ '32 Clarence Road, , , , Moseley, Birmingham, West Midlands',
+ $this->address->toString()
+ );
+ }
+
+ /** @test */
+ public function it_can_cast_the_address_to_a_string(): void
+ {
+ $this->assertEquals(
+ '32 Clarence Road, , , , Moseley, Birmingham, West Midlands',
+ (string) $this->address
+ );
+ }
+
+ /** @test */
+ public function it_can_can_check_if_two_address_responses_are_the_same(): void
+ {
+ $address = ResponseFactory::make('singleFindResponse.json')
+ ->makeAddressCollectionResponse()->getAddresses()[0];
+
+ $this->assertTrue($this->address->sameAs($address));
+ }
+}
diff --git a/tests/Unit/Responses/ExpandedAddressTest.php b/tests/Unit/Responses/ExpandedAddressTest.php
new file mode 100644
index 0000000..25630de
--- /dev/null
+++ b/tests/Unit/Responses/ExpandedAddressTest.php
@@ -0,0 +1,154 @@
+address = ResponseFactory::make('successfulGetResponse.json', true)
+ ->makeSingleAddressCollectionResponse()->getAddress();
+ }
+
+ /** @test */
+ public function it_can_get_the_thoroughfare(): void
+ {
+ $this->assertEquals('Clarence Road', $this->address->getThoroughfare());
+ }
+
+ /** @test */
+ public function it_can_get_the_building_name(): void
+ {
+ $this->assertEquals('Clarence Mill', $this->address->getBuildingName());
+ }
+
+ /** @test */
+ public function it_can_get_the_sub_building_name(): void
+ {
+ $this->assertEquals('', $this->address->getSubBuildingName());
+ }
+
+ /** @test */
+ public function it_can_get_the_building_number(): void
+ {
+ $this->assertEquals('', $this->address->getBuildingNumber());
+ }
+
+ /** @test */
+ public function it_can_get_the_sub_building_number(): void
+ {
+ $this->assertEquals('32', $this->address->getSubBuildingNumber());
+ }
+
+ /** @test */
+ public function it_can_get_line_one(): void
+ {
+ $this->assertEquals('Apartment 32', $this->address->getLine1());
+ }
+
+ /** @test */
+ public function it_can_get_line_two(): void
+ {
+ $this->assertEquals('Clarence Mill', $this->address->getLine2());
+ }
+
+ /** @test */
+ public function it_can_get_line_three(): void
+ {
+ $this->assertEquals('Clarence Road', $this->address->getLine3());
+ }
+
+ /** @test */
+ public function it_can_get_line_four(): void
+ {
+ $this->assertEquals('', $this->address->getLine4());
+ }
+
+ /** @test */
+ public function it_can_get_a_line_by_the_given_number(): void
+ {
+ $this->assertEquals('Clarence Road', $this->address->getLine(3));
+ $this->assertNull($this->address->getLine(5));
+ }
+
+ /** @test */
+ public function it_can_get_the_locality(): void
+ {
+ $this->assertEquals('Bollington', $this->address->getLocality());
+ }
+
+ /** @test */
+ public function it_can_get_the_town(): void
+ {
+ $this->assertEquals('Macclesfield', $this->address->getTown());
+ }
+
+ /** @test */
+ public function it_can_get_the_city(): void
+ {
+ $this->assertEquals('Macclesfield', $this->address->getCity());
+ }
+
+ /** @test */
+ public function it_can_get_the_county(): void
+ {
+ $this->assertEquals('Cheshire', $this->address->getCounty());
+ }
+
+ /** @test */
+ public function it_can_get_the_district(): void
+ {
+ $this->assertEquals('Cheshire East', $this->address->getDistrict());
+ }
+
+ /** @test */
+ public function it_can_get_the_country(): void
+ {
+ $this->assertEquals('England', $this->address->getCountry());
+ }
+
+ /** @test */
+ public function it_can_transform_expanded_address_to_array(): void
+ {
+ $response = ResponseFactory::make('successfulGetResponse.json', true)->getDecodedResponse();
+ $this->assertEquals(
+ array_merge($response, ['formatted_string' => $this->address->toString(true)]),
+ $this->address->toArray()
+ );
+ }
+
+ /** @test */
+ public function it_can_transform_expanded_address_to_string_and_remove_empty_elements(): void
+ {
+ $this->assertEquals(
+ 'Apartment 32, Clarence Mill, Clarence Road, Bollington, Macclesfield, Cheshire',
+ $this->address->toString(true)
+ );
+ }
+
+ /** @test */
+ public function it_can_transform_expanded_address_to_string(): void
+ {
+ $this->assertEquals(
+ 'Apartment 32, Clarence Mill, Clarence Road, Bollington, Macclesfield, Cheshire',
+ $this->address->toString()
+ );
+ }
+
+ /** @test */
+ public function it_can_cast_the_expanded_address_to_a_string(): void
+ {
+ $this->assertEquals(
+ 'Apartment 32, Clarence Mill, Clarence Road, Bollington, Macclesfield, Cheshire',
+ (string) $this->address
+ );
+ }
+}
diff --git a/tests/Unit/Responses/SingleAddressCollectionResponseTest.php b/tests/Unit/Responses/SingleAddressCollectionResponseTest.php
new file mode 100644
index 0000000..c298c5c
--- /dev/null
+++ b/tests/Unit/Responses/SingleAddressCollectionResponseTest.php
@@ -0,0 +1,28 @@
+makeSingleAddressCollectionResponse();
+
+ $address = $response->getAddress();
+ $this->assertEquals('Apartment 32', $address->getLine1());
+ $this->assertEquals('Clarence Mill', $address->getLine2());
+ $this->assertEquals('Clarence Road', $address->getLine3());
+ $this->assertEquals('', $address->getLine4());
+ $this->assertEquals('Bollington', $address->getLocality());
+ $this->assertEquals('Macclesfield', $address->getTown());
+ $this->assertEquals('Macclesfield', $address->getCity());
+ $this->assertEquals('Cheshire', $address->getCounty());
+ $this->assertEquals('Cheshire East', $address->getDistrict());
+ $this->assertEquals('England', $address->getCountry());
+ $this->assertTrue($address->isResidential());
+ }
+}
diff --git a/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array__1.json b/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array__1.json
new file mode 100644
index 0000000..02d65d8
--- /dev/null
+++ b/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array__1.json
@@ -0,0 +1,21 @@
+{
+ "postcode": "B13 9SZ",
+ "latitude": 52.437353914285715,
+ "longitude": -1.8828399142857144,
+ "addresses": [
+ "32 Clarence Road, Moseley, Birmingham, West Midlands",
+ "32a Clarence Road, Moseley, Birmingham, West Midlands",
+ "34 Clarence Road, Moseley, Birmingham, West Midlands",
+ "36 Clarence Road, Moseley, Birmingham, West Midlands",
+ "38 Clarence Road, Moseley, Birmingham, West Midlands",
+ "40 Clarence Road, Moseley, Birmingham, West Midlands",
+ "41 Clarence Road, Moseley, Birmingham, West Midlands",
+ "43 Clarence Road, Moseley, Birmingham, West Midlands",
+ "45 Clarence Road, Moseley, Birmingham, West Midlands",
+ "47 Clarence Road, Moseley, Birmingham, West Midlands",
+ "49 Clarence Road, Moseley, Birmingham, West Midlands",
+ "51 Clarence Road, Moseley, Birmingham, West Midlands",
+ "Flat 1, 36 Clarence Road, Moseley, Birmingham, West Midlands",
+ "Flat 2, 36 Clarence Road, Moseley, Birmingham, West Midlands"
+ ]
+}
diff --git a/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array_with_expanded_addresses__1.json b/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array_with_expanded_addresses__1.json
new file mode 100644
index 0000000..d98e9ac
--- /dev/null
+++ b/tests/Unit/Responses/__snapshots__/AddressCollectionResponseTest__it_can_transform_to_the_expected_array_with_expanded_addresses__1.json
@@ -0,0 +1,147 @@
+{
+ "postcode": "B13 9SZ",
+ "latitude": 52.437353914285715,
+ "longitude": -1.8828399142857144,
+ "addresses": [
+ {
+ "formatted_string": "32 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "32 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "32a Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "32a Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "34 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "34 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "36 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "36 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "38 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "38 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "40 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "40 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "41 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "41 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "43 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "43 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "45 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "45 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "47 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "47 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "49 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "49 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "51 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "51 Clarence Road",
+ "line_2": " ",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "Flat 1, 36 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "Flat 1",
+ "line_2": " 36 Clarence Road",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ },
+ {
+ "formatted_string": "Flat 2, 36 Clarence Road, Moseley, Birmingham, West Midlands",
+ "line_1": "Flat 2",
+ "line_2": " 36 Clarence Road",
+ "line_3": " ",
+ "line_4": " ",
+ "locality": " Moseley",
+ "town_or_city": " Birmingham",
+ "county": " West Midlands"
+ }
+ ]
+}