base_adresse: add parameter type=housenumber to prevent users from picking street addresses (#76376)
gitea/passerelle/pipeline/head This commit looks good Details

This commit is contained in:
Benjamin Dauvergne 2023-04-14 16:40:01 +02:00
parent 8e215185ec
commit 84cd51957e
2 changed files with 70 additions and 7 deletions

View File

@ -125,10 +125,24 @@ class BaseAdresse(AddressResource):
'Prioritize results according to coordinates. "lat" parameter must also be present.'
)
},
'type': {
'description': _(
'Type of address to return, housenumber, street, locality, municipality or all. Default is all.'
)
},
},
)
def addresses(
self, request, id=None, q=None, zipcode='', citycode=None, lat=None, lon=None, page_limit=5
self,
request,
id=None,
q=None,
zipcode='',
citycode=None,
lat=None,
lon=None,
page_limit=5,
type=None,
):
if id is not None:
return self.get_by_id(request, id=id, citycode=citycode)
@ -156,6 +170,8 @@ class BaseAdresse(AddressResource):
if self.latitude and self.longitude or lat and lon:
query_args['lat'] = lat or self.latitude
query_args['lon'] = lon or self.longitude
if type in ('housenumber', 'street', 'locality', 'municipality'):
query_args['type'] = type
query = urlencode(query_args)
url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
@ -167,7 +183,8 @@ class BaseAdresse(AddressResource):
result = []
for feature in result_response.json().get('features'):
features = result_response.json().get('features')
for feature in features:
if not feature['geometry']['type'] == 'Point':
continue # skip unknown
data = self.format_address_data(feature)
@ -177,7 +194,6 @@ class BaseAdresse(AddressResource):
)
if not created:
address.update_timestamp()
return {'data': result}
def get_by_id(self, request, id, citycode=None):
@ -236,13 +252,25 @@ class BaseAdresse(AddressResource):
'Prioritize results according to coordinates. "lon" parameter must be present.'
)
},
'type': {
'description': _(
'Type of address to return, housenumber, street, locality, municipality or all. Default is all.'
)
},
},
)
def search(self, request, q, zipcode='', citycode=None, lat=None, lon=None, **kwargs):
def search(self, request, q, zipcode='', citycode=None, lat=None, lon=None, type=None, **kwargs):
if kwargs.get('format', 'json') != 'json':
raise NotImplementedError()
result = self.addresses(
request, q=q, zipcode=zipcode, citycode=citycode, lat=lat, lon=lon, page_limit=1
request,
q=q,
zipcode=zipcode,
citycode=citycode,
lat=lat,
lon=lon,
page_limit=1,
type=type,
)
return result['data']
@ -251,15 +279,23 @@ class BaseAdresse(AddressResource):
parameters={
'lat': {'description': _('Latitude'), 'example_value': 48.833708},
'lon': {'description': _('Longitude'), 'example_value': 2.323349},
'type': {
'description': _(
'Type of address to return, housenumber, street, locality, municipality or all. Default is all.'
)
},
},
)
def reverse(self, request, lat, lon, **kwargs):
def reverse(self, request, lat, lon, type=None, **kwargs):
if kwargs.get('format', 'json') != 'json':
raise NotImplementedError()
scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url)
path = urlparse.urljoin(path, 'reverse/')
query = urlencode({'lat': lat, 'lon': lon})
query_dict = {'lat': lat, 'lon': lon}
if type in ('housenumber', 'street', 'locality', 'municipality'):
query_dict['type'] = type
query = urlencode(query_dict)
url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
try:

View File

@ -211,6 +211,21 @@ def test_base_adresse_search(mocked_get, app, base_adresse):
assert data['display_name'] == 'Rue Roger Halope 49000 Angers'
@mock.patch('passerelle.utils.Request.get')
def test_base_adresse_search_type_housenumber(mocked_get, app, base_adresse):
endpoint = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug)
mocked_get.return_value = tests.utils.FakedResponse(content=FAKED_CONTENT, status_code=200)
app.get(endpoint, params={'q': 'plop'}, status=200)
assert 'type=' not in mocked_get.call_args[0][0]
for type in ['housenumber', 'street', 'locality', 'municipality']:
app.get(endpoint, params={'q': 'plop', 'type': type}, status=200)
assert f'type={type}' in mocked_get.call_args[0][0]
app.get(endpoint, params={'q': 'plop', 'type': 'foo'}, status=200)
assert 'type=foo' not in mocked_get.call_args[0][0]
@mock.patch('passerelle.utils.Request.get')
def test_base_adresse_search_limit_to_200(mocked_get, app, base_adresse):
endpoint = tests.utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug)
@ -366,6 +381,18 @@ def test_base_adresse_reverse_path(mocked_get, app, base_adresse):
assert mocked_get.call_args[0][0].startswith('http://example.net/path/reverse/?')
@mock.patch('passerelle.utils.Request.get')
def test_base_adresse_reverse_type_housenumber(mocked_get, app, base_adresse):
mocked_get.return_value = tests.utils.FakedResponse(content=json.dumps({'features': []}), status_code=200)
app.get('/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633' % base_adresse.slug)
assert 'type=' not in mocked_get.call_args[0][0]
app.get('/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633&type=truc' % base_adresse.slug)
assert 'type=' not in mocked_get.call_args[0][0]
for type in ['housenumber', 'street', 'locality', 'municipality']:
app.get(f'/base-adresse/%s/reverse?lon=-0.593775&lat=47.474633&type={type}' % base_adresse.slug)
assert f'type={type}' in mocked_get.call_args[0][0]
@mock.patch('passerelle.utils.Request.get')
def test_base_adresse_reverse_api_timeout(mocked_get, app, base_adresse):
mocked_get.side_effect = ConnectionError('Remote end closed connection without response')