diff --git a/.github/workflows/provider.yml b/.github/workflows/provider.yml index 06d328d..3503f48 100644 --- a/.github/workflows/provider.yml +++ b/.github/workflows/provider.yml @@ -29,7 +29,7 @@ jobs: - name: composer-cache id: composer-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: vendor key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/src/Adgfr.php b/src/Adgfr.php index f203d83..a4ade79 100644 --- a/src/Adgfr.php +++ b/src/Adgfr.php @@ -5,7 +5,6 @@ namespace Geocoder\Provider\Adgfr; use Geocoder\Collection; -use Geocoder\Exception\InvalidArgument; use Geocoder\Exception\InvalidServerResponse; use Geocoder\Exception\UnsupportedOperation; use Geocoder\Http\Provider\AbstractHttpProvider; @@ -24,96 +23,78 @@ final class Adgfr extends AbstractHttpProvider public function geocodeQuery(GeocodeQuery $query): Collection { - $address = $query->getText(); - // This API doesn't handle IPs - if (filter_var($address, \FILTER_VALIDATE_IP)) { + if (filter_var($query->getText(), \FILTER_VALIDATE_IP)) { throw new UnsupportedOperation('The Adgfr provider does not support IP addresses.'); } - $url = sprintf('%s/search/?q=%s', self::API_URL, urldecode($address)); - - if ($limit = $query->getLimit()) { - $url .= sprintf('&limit=%d', $limit); - } - - if ($autocomplete = $query->getData('autocomplete')) { - $url .= sprintf('&autocomplete=%d', $autocomplete); - } - - if ( - ($lat = $query->getData('lat')) - && ($lon = $query->getData('lon')) - ) { - $url .= sprintf('&lat=%F&lon=%F', $lat, $lon); - } - - if ($type = $query->getData('type')) { - if (\is_string($type)) { - $type = Type::from($type); - } - - if (!$type instanceof Type) { - throw new InvalidArgument(sprintf('"type" must be a valid "%s" enum, "%s" given.', Type::class, get_debug_type($type))); - } - - $url .= sprintf('&type=%s', $type->value); - } + $url = sprintf( + '%s/search/?%s', + self::API_URL, + http_build_query([ + 'q' => $query->getText(), + 'limit' => $query->getLimit(), + 'lat' => $query->getData('lat'), + 'lon' => $query->getData('lon'), + 'postcode' => $query->getData('postcode'), + 'citycode' => $query->getData('citycode'), + 'type' => Type::tryFrom($query->getData('type', ''))?->value, + ], '', '&', \PHP_QUERY_RFC3986) + ); + + return $this->executeQuery($url); + } - if ($postcode = $query->getData('postcode')) { - $url .= sprintf('&postcode=%s', $postcode); - } + public function reverseQuery(ReverseQuery $query): Collection + { + $coordinates = $query->getCoordinates(); + + $url = sprintf( + '%s/reverse/?%s', + self::API_URL, + http_build_query([ + 'lat' => $coordinates->getLatitude(), + 'lon' => $coordinates->getLongitude(), + ], '', '&', \PHP_QUERY_RFC3986) + ); + + return $this->executeQuery($url); + } - if ($citycode = $query->getData('citycode')) { - $url .= sprintf('&citycode=%s', $citycode); - } + public function getName(): string + { + return self::PROVIDER_NAME; + } - $content = $this->executeQuery($url); + private function executeQuery(string $url): AddressCollection + { + $content = $this->getParsedResponse($this->getRequest($url)); - $json = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + $json = json_decode($content, false, 512, \JSON_THROW_ON_ERROR); - if (!is_object($json)) { + if (!$json instanceof \stdClass) { throw InvalidServerResponse::create($url); } - if (empty($json)) { - return new AddressCollection([]); - } - $results = []; foreach ($json->features as $feature) { - $results[] = $this->jsonResultToLocation($feature, false); + $results[] = $this->jsonResultToLocation($feature); } return new AddressCollection($results); } - public function reverseQuery(ReverseQuery $query): Collection - { - return new AddressCollection([]); - } - - public function getName(): string - { - return self::PROVIDER_NAME; - } - - private function executeQuery(string $url): string - { - return $this->getParsedResponse($this->getRequest($url)); - } - - private function jsonResultToLocation(\stdClass $feature, bool $reverse): Location + private function jsonResultToLocation(\stdClass $feature): Location { - [$latitude, $longitude] = $feature->geometry->coordinates; + [$longitude, $latitude] = $feature->geometry->coordinates; $builder = new AddressBuilder($this->getName()); $builder ->setCoordinates($latitude, $longitude) ->setStreetNumber($feature->properties->housenumber ?? null) ->setStreetName($feature->properties->street ?? null) - ->setPostalCode($feature->properties->postcode) - ->setLocality($feature->properties->city) + ->setPostalCode($feature->properties->postcode ?? null) + ->setLocality($feature->properties->city ?? null) ->setCountry('France') ->setCountryCode('FR') ; diff --git a/tests/.cache-response/.gitkeep b/tests/.cached_responses/.gitkeep similarity index 100% rename from tests/.cache-response/.gitkeep rename to tests/.cached_responses/.gitkeep diff --git a/tests/.cached_responses/api-adresse.data.gouv.fr_5585d33013cc2bc65e8433212c3b6e82f0506a5d b/tests/.cached_responses/api-adresse.data.gouv.fr_5585d33013cc2bc65e8433212c3b6e82f0506a5d new file mode 100644 index 0000000..ef06d6c --- /dev/null +++ b/tests/.cached_responses/api-adresse.data.gouv.fr_5585d33013cc2bc65e8433212c3b6e82f0506a5d @@ -0,0 +1 @@ +s:157:"{"type":"FeatureCollection","version":"draft","features":[],"attribution":"BAN","licence":"ETALAB-2.0","query":"jsajhgsdkfjhsfkjhaldkadjaslgldasd","limit":5}"; \ No newline at end of file diff --git a/tests/.cached_responses/api-adresse.data.gouv.fr_faf7bfaf865c8f2269375527ee0dd9f8214b707e b/tests/.cached_responses/api-adresse.data.gouv.fr_faf7bfaf865c8f2269375527ee0dd9f8214b707e new file mode 100644 index 0000000..c176670 --- /dev/null +++ b/tests/.cached_responses/api-adresse.data.gouv.fr_faf7bfaf865c8f2269375527ee0dd9f8214b707e @@ -0,0 +1 @@ +s:113:"{"type":"FeatureCollection","version":"draft","features":[],"attribution":"BAN","licence":"ETALAB-2.0","limit":1}"; \ No newline at end of file diff --git a/tests/AdgfrTest.php b/tests/AdgfrTest.php index f9175e2..44c6275 100644 --- a/tests/AdgfrTest.php +++ b/tests/AdgfrTest.php @@ -10,8 +10,10 @@ use Geocoder\Model\Address; use Geocoder\Model\AddressCollection; use Geocoder\Provider\Adgfr\Adgfr; +use Geocoder\Provider\Adgfr\Enum\Type; use Geocoder\Provider\Adgfr\Model\AdgfrAddress; use Geocoder\Query\GeocodeQuery; +use Geocoder\Query\ReverseQuery; final class AdgfrTest extends BaseTestCase { @@ -35,11 +37,91 @@ public function testGeocodeWithRealAddress(): void $this->assertInstanceOf(AddressCollection::class, $results); $this->assertCount(5, $results); + /** @var AdgfrAddress $address */ + $address = $results->first(); + $this->assertInstanceOf(Address::class, $address); + $this->assertEqualsWithDelta(48.850699, $address->getCoordinates()->getLatitude(), 0.00001); + $this->assertEqualsWithDelta(2.308628, $address->getCoordinates()->getLongitude(), 0.00001); + $this->assertSame('20', $address->getStreetNumber()); + $this->assertSame('Avenue de Ségur', $address->getStreetName()); + $this->assertSame('75007', $address->getPostalCode()); + $this->assertSame('Paris', $address->getLocality()); + $this->assertSame('FR', $address->getCountry()->getCode()); + $this->assertSame('75107_8909_00020', $address->getId()); + $this->assertSame(Type::HouseNumber, $address->getType()); + $this->assertSame('75107', $address->getCityCode()); + $this->assertSame('Paris 7e Arrondissement', $address->getDistrict()); + } + + public function testGeocodeHouseNumberTypeQuery(): void + { + $provider = new Adgfr($this->getHttpClient()); + $results = $provider->geocodeQuery( + GeocodeQuery::create('20 avenue Kléber, Paris')->withData('type', Type::HouseNumber->value) + ); + + $this->assertInstanceOf(AddressCollection::class, $results); + + /** @var AdgfrAddress $address */ + $address = $results->first(); + $this->assertInstanceOf(Address::class, $address); + $this->assertSame('20', $address->getStreetNumber()); + $this->assertSame('Avenue Kléber', $address->getStreetName()); + $this->assertSame('75016', $address->getPostalCode()); + $this->assertSame('Paris', $address->getLocality()); + } + + public function testGeocodeStreetTypeQuery(): void + { + $provider = new Adgfr($this->getHttpClient()); + $results = $provider->geocodeQuery( + GeocodeQuery::create('20 avenue Kléber, Paris')->withData('type', Type::Street->value) + ); + + $this->assertInstanceOf(AddressCollection::class, $results); + + /** @var AdgfrAddress $address */ + $address = $results->first(); + $this->assertInstanceOf(Address::class, $address); + $this->assertNull($address->getStreetNumber()); + $this->assertSame('Avenue Kléber', $address->getStreetName()); + $this->assertSame('75016', $address->getPostalCode()); + $this->assertSame('Paris', $address->getLocality()); + } + + public function testGeocodeLocalityQuery(): void + { + $provider = new Adgfr($this->getHttpClient()); + $results = $provider->geocodeQuery( + GeocodeQuery::create('20 avenue Kléber, Paris')->withData('type', Type::Locality->value) + ); + + $this->assertInstanceOf(AddressCollection::class, $results); + + /** @var AdgfrAddress $address */ + $address = $results->first(); + $this->assertInstanceOf(Address::class, $address); + $this->assertNull($address->getStreetNumber()); + $this->assertNull($address->getStreetName()); + $this->assertEqualsWithDelta(48.994028, $address->getCoordinates()->getLatitude(), 0.00001); + $this->assertEqualsWithDelta(2.509919, $address->getCoordinates()->getLongitude(), 0.00001); + $this->assertSame('95700', $address->getPostalCode()); + $this->assertSame('Roissy-en-France', $address->getLocality()); + } + + public function testReverseWithCoordinates(): void + { + $provider = new Adgfr($this->getHttpClient()); + $results = $provider->reverseQuery(ReverseQuery::fromCoordinates(48.850699, 2.308628)); + + $this->assertInstanceOf(AddressCollection::class, $results); + $this->assertCount(5, $results); + /** @var AdgfrAddress $result */ $address = $results->first(); $this->assertInstanceOf(Address::class, $address); - $this->assertEqualsWithDelta(2.308628, $address->getCoordinates()->getLatitude(), 0.00001); - $this->assertEqualsWithDelta(48.850699, $address->getCoordinates()->getLongitude(), 0.00001); + $this->assertEqualsWithDelta(48.850699, $address->getCoordinates()->getLatitude(), 0.00001); + $this->assertEqualsWithDelta(2.308628, $address->getCoordinates()->getLongitude(), 0.00001); $this->assertSame('20', $address->getStreetNumber()); $this->assertSame('Avenue de Ségur', $address->getStreetName()); $this->assertSame('75007', $address->getPostalCode()); diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php new file mode 100644 index 0000000..1ffe2d1 --- /dev/null +++ b/tests/IntegrationTest.php @@ -0,0 +1,48 @@ + 'This provider supports France only.', + 'testReverseQuery' => 'This provider supports France only.', + ]; + + protected function createProvider(ClientInterface $httpClient): Adgfr + { + return new Adgfr($httpClient); + } + + protected function getCacheDir(): string + { + return __DIR__.'/.cached_responses'; + } + + protected function getApiKey(): string + { + return ''; + } +}