diff --git a/idunn/api/directions.py b/idunn/api/directions.py index c8ae6f270..f6b33d1cd 100644 --- a/idunn/api/directions.py +++ b/idunn/api/directions.py @@ -1,15 +1,13 @@ -from fastapi import HTTPException - -from pydantic import constr - +from fastapi import HTTPException, Query, Depends from starlette.requests import Request from starlette.responses import Response from idunn import settings -from idunn.utils.geometry import city_surrounds_polygons +from idunn.places import Latlon, place_from_id, InvalidPlaceId from idunn.utils.rate_limiter import IdunnRateLimiter from ..directions.client import directions_client + rate_limiter = IdunnRateLimiter( resource="idunn.api.directions", max_requests=int(settings["DIRECTIONS_RL_MAX_REQUESTS"]), @@ -17,21 +15,53 @@ ) -def get_directions( - response: Response, +def directions_request(request: Request, response: Response): + """ + FastAPI Dependency + Responsible for rate limit and cache headers for directions requests + """ + rate_limiter.check_limit_per_client(request) + response.headers["cache-control"] = "max-age={}".format(settings["DIRECTIONS_CLIENT_CACHE"]) + return request + + +def get_directions_with_coordinates( + # URL values f_lon: float, f_lat: float, t_lon: float, - t_lat: float, # URL values - request: Request, - type: constr(min_length=1), - language: str = "en", # query parameters + t_lat: float, + # Query parameters + type: str, + language: str = "en", + # Request + request: Request = Depends(directions_request), +): + from_place = Latlon(f_lat, f_lon) + to_place = Latlon(t_lat, t_lon) + if not type: + raise HTTPException(status_code=400, detail='"type" query param is required') + return directions_client.get_directions( + from_place, to_place, type, language, params=request.query_params + ) + + +def get_directions( + # Query parameters + origin: str = Query(..., description="Origin place id"), + destination: str = Query(..., description="Destination place id"), + type: str = Query(..., description="Transport mode"), + language: str = Query("en", description="User language"), + # Request + request: Request = Depends(directions_request), ): rate_limiter.check_limit_per_client(request) - from_position = (f_lon, f_lat) - to_position = (t_lon, t_lat) + try: + from_place = place_from_id(origin) + to_place = place_from_id(destination) + except InvalidPlaceId as exc: + raise HTTPException(status_code=404, detail=exc.message) - response.headers["cache-control"] = "max-age={}".format(settings["DIRECTIONS_CLIENT_CACHE"]) return directions_client.get_directions( - from_position, to_position, type, language, params=request.query_params + from_place, to_place, type, language, params=request.query_params ) diff --git a/idunn/api/pages_jaunes.py b/idunn/api/pages_jaunes.py index 2e98c8e4c..50d85be93 100644 --- a/idunn/api/pages_jaunes.py +++ b/idunn/api/pages_jaunes.py @@ -10,7 +10,7 @@ class PjSource: - PLACE_ID_PREFIX = "pj:" + PLACE_ID_NAMESPACE = "pj" es_index = settings.get("PJ_ES_INDEX") es_query_template = settings.get("PJ_ES_QUERY_TEMPLATE") @@ -54,7 +54,7 @@ def get_places_bbox(self, raw_categories, bbox, size=10, query=""): return [PjPOI(p["_source"]) for p in raw_places] def get_place(self, id): - internal_id = id.replace(self.PLACE_ID_PREFIX, "", 1) + internal_id = id.replace(f"{self.PLACE_ID_NAMESPACE}:", "", 1) es_places = self.es.search( index=self.es_index, body={"filter": {"term": {"_id": internal_id}}} diff --git a/idunn/api/places.py b/idunn/api/places.py index f54917807..800551cbd 100644 --- a/idunn/api/places.py +++ b/idunn/api/places.py @@ -1,4 +1,3 @@ -import json import logging import urllib.parse @@ -7,13 +6,11 @@ from starlette.requests import Request from idunn import settings -from idunn.utils import prometheus from idunn.utils.es_wrapper import get_elasticsearch -from idunn.utils.index_names import INDICES -from idunn.places import Place, Admin, Street, Address, POI, Latlon + +from idunn.places import Place, Latlon, place_from_id from idunn.places.base import BasePlace -from idunn.api.utils import fetch_es_place, DEFAULT_VERBOSITY, ALL_VERBOSITY_LEVELS -from idunn.api.pages_jaunes import pj_source +from idunn.api.utils import DEFAULT_VERBOSITY, ALL_VERBOSITY_LEVELS from .closest import get_closest_place @@ -80,34 +77,9 @@ def get_place( id: str, request: Request, lang: str = None, type=None, verbosity=DEFAULT_VERBOSITY ) -> Place: """Main handler that returns the requested place""" - es = get_elasticsearch() verbosity = validate_verbosity(verbosity) lang = validate_lang(lang) - - # Handle place from "pages jaunes" - if id.startswith(pj_source.PLACE_ID_PREFIX): - pj_place = pj_source.get_place(id) - log_place_request(pj_place, request.headers) - return pj_place.load_place(lang, verbosity) - - #  Otherwise handle places from the ES db - es_place = fetch_es_place(id, es, INDICES, type) - - places = { - "admin": Admin, - "street": Street, - "addr": Address, - "poi": POI, - } - loader = places.get(es_place.get("_type")) - - if loader is None: - prometheus.exception("FoundPlaceWithWrongType") - raise Exception( - "Place with id '{}' has a wrong type: '{}'".format(id, es_place[0].get("_type")) - ) - - place = loader(es_place["_source"]) + place = place_from_id(id, type) log_place_request(place, request.headers) return place.load_place(lang, verbosity) diff --git a/idunn/api/pois.py b/idunn/api/pois.py index a1bf4f4d2..c0439e705 100644 --- a/idunn/api/pois.py +++ b/idunn/api/pois.py @@ -1,9 +1,6 @@ -from elasticsearch import Elasticsearch - from idunn import settings from idunn.places import POI from idunn.utils.es_wrapper import get_elasticsearch -from idunn.utils.settings import Settings from idunn.api.utils import fetch_es_poi, DEFAULT_VERBOSITY diff --git a/idunn/api/urls.py b/idunn/api/urls.py index 135da340d..85286ee92 100644 --- a/idunn/api/urls.py +++ b/idunn/api/urls.py @@ -4,10 +4,10 @@ from .places_list import get_places_bbox, get_events_bbox from .categories import get_all_categories from .closest import closest_address -from .directions import get_directions from ..directions.models import DirectionsResponse from .geocoder import get_autocomplete from ..geocoder.models import GeocodeJson +from .directions import get_directions_with_coordinates, get_directions from ..utils.prometheus import ( expose_metrics, expose_metrics_multiprocess, @@ -33,19 +33,28 @@ def get_api_urls(settings): return [ APIRoute("/metrics", metric_handler), APIRoute("/status", get_status), - # Deprecated - APIRoute("/pois/{id}", get_poi), + # Deprecated POI route + APIRoute("/pois/{id}", get_poi, deprecated=True), + # Places APIRoute("/places", get_places_bbox), APIRoute("/places/latlon:{lat}:{lon}", get_place_latlon), APIRoute("/places/{id}", handle_option, methods=["OPTIONS"]), APIRoute("/places/{id}", get_place), + # Categories APIRoute("/categories", get_all_categories), + # Reverse APIRoute("/reverse/{lat}:{lon}", closest_address), # Kuzzle events APIRoute("/events", get_events_bbox), # Directions APIRoute( "/directions/{f_lon},{f_lat};{t_lon},{t_lat}", + get_directions_with_coordinates, + response_model=DirectionsResponse, + responses={422: {"description": "Requested Path Not Allowed."}}, + ), + APIRoute( + "/directions", get_directions, response_model=DirectionsResponse, responses={422: {"description": "Requested Path Not Allowed."}}, diff --git a/idunn/api/utils.py b/idunn/api/utils.py index d4c8a1af2..e40e6af1f 100644 --- a/idunn/api/utils.py +++ b/idunn/api/utils.py @@ -23,6 +23,7 @@ WikiUndefinedException, ) from idunn.utils import prometheus +from idunn.utils.index_names import INDICES import phonenumbers logger = logging.getLogger(__name__) @@ -138,7 +139,9 @@ def fetch_es_poi(id, es) -> dict: This function gets from Elasticsearch the entry corresponding to the given id. """ - es_pois = es.search(index=PLACE_POI_INDEX, body={"filter": {"term": {"_id": id}}}) + es_pois = es.search( + index=PLACE_POI_INDEX, body={"filter": {"term": {"_id": id}}}, ignore_unavailable=True + ) es_poi = es_pois.get("hits", {}).get("hits", []) if len(es_poi) == 0: @@ -192,13 +195,14 @@ def fetch_bbox_places(es, indices, raw_filters, bbox, max_size) -> list: }, size=max_size, timeout="3s", + ignore_unavailable=True, ) bbox_places = bbox_places.get("hits", {}).get("hits", []) return bbox_places -def fetch_es_place(id, es, indices, type) -> dict: +def fetch_es_place(id, es, type) -> dict: """Returns the raw Place data This function gets from Elasticsearch the @@ -206,20 +210,26 @@ def fetch_es_place(id, es, indices, type) -> dict: """ if type is None: index_name = PLACE_DEFAULT_INDEX - elif type not in indices: + elif type not in INDICES: raise HTTPException(status_code=400, detail=f"Wrong type parameter: type={type}") else: - index_name = indices.get(type) + index_name = INDICES.get(type) try: - es_places = es.search(index=index_name, body={"filter": {"term": {"_id": id}}}) + es_places = es.search( + index=index_name, body={"filter": {"term": {"_id": id}}}, ignore_unavailable=True, + ) except ElasticsearchException as error: logger.warning(f"error with database: {error}") raise HTTPException(detail="database issue", status_code=503) es_place = es_places.get("hits", {}).get("hits", []) if len(es_place) == 0: - raise HTTPException(status_code=404, detail=f"place {id} not found with type={type}") + if type is None: + message = f"place '{id}' not found" + else: + message = f"place '{id}' not found with type={type}" + raise HTTPException(status_code=404, detail=message) if len(es_place) > 1: logger.warning("Got multiple places with id %s", id) diff --git a/idunn/directions/client.py b/idunn/directions/client.py index ab1039765..325333b57 100644 --- a/idunn/directions/client.py +++ b/idunn/directions/client.py @@ -10,6 +10,7 @@ from shapely.geometry import Point from idunn import settings from idunn.utils.geometry import city_surrounds_polygons +from idunn.places.base import BasePlace from .models import DirectionsResponse logger = logging.getLogger(__name__) @@ -49,24 +50,34 @@ def MAPBOX_API_ENABLED(self): return bool(settings["MAPBOX_DIRECTIONS_ACCESS_TOKEN"]) @staticmethod - def is_in_allowed_zone(mode: str, from_loc: (float, float), to_loc: (float, float)): + def is_in_allowed_zone(mode: str, from_place: BasePlace, to_place: BasePlace): if mode == "publictransport" and settings["PUBLIC_TRANSPORTS_RESTRICT_TO_CITIES"]: + from_coord = from_place.get_coord() + to_coord = to_place.get_coord() return any( all( city_surrounds_polygons[city].contains(point) - for point in [Point(*from_loc), Point(*to_loc)] + for point in [ + Point(from_coord["lon"], from_coord["lat"]), + Point(to_coord["lon"], to_coord["lat"]), + ] ) for city in settings["PUBLIC_TRANSPORTS_RESTRICT_TO_CITIES"].split(",") ) - return True + def place_to_url_coords(self, place): + coord = place.get_coord() + lat, lon = coord["lat"], coord["lon"] + return (f"{lon:.5f}", f"{lat:.5f}") + def directions_mapbox(self, start, end, mode, lang, extra=None): if extra is None: extra = {} - start_lon, start_lat = start - end_lon, end_lat = end + start_lon, start_lat = self.place_to_url_coords(start) + end_lon, end_lat = self.place_to_url_coords(end) + base_url = settings["MAPBOX_DIRECTIONS_API_BASE_URL"] response = self.session.get( f"{base_url}/{mode}/{start_lon},{start_lat};{end_lon},{end_lat}", @@ -81,25 +92,25 @@ def directions_mapbox(self, start, end, mode, lang, extra=None): if 400 <= response.status_code < 500: # Proxy client errors logger.info( - "Got error from mapbox API. " "Status: %s, Body: %s", + "Got error from mapbox API. Status: %s, Body: %s", response.status_code, response.text, ) - return JSONResponse(content=response.json(), status_code=response.status_code,) + return JSONResponse(content=response.json(), status_code=response.status_code) response.raise_for_status() return DirectionsResponse(status="success", data=response.json()) def directions_qwant(self, start, end, mode, lang, extra=None): if not self.QWANT_BASE_URL: raise HTTPException( - status_code=501, detail=f"Directions API is currently unavailable for mode {mode}", + status_code=501, detail=f"Directions API is currently unavailable for mode {mode}" ) if extra is None: extra = {} - start_lon, start_lat = start - end_lon, end_lat = end + start_lon, start_lat = self.place_to_url_coords(start) + end_lon, end_lat = self.place_to_url_coords(end) response = self.session.get( f"{self.QWANT_BASE_URL}/{start_lon},{start_lat};{end_lon},{end_lat}", params={ @@ -112,19 +123,29 @@ def directions_qwant(self, start, end, mode, lang, extra=None): if 400 <= response.status_code < 500: # Proxy client errors - return JSONResponse(content=response.json(), status_code=response.status_code,) + return JSONResponse(content=response.json(), status_code=response.status_code) response.raise_for_status() return DirectionsResponse(**response.json()) + def place_to_combigo_location(self, place, lang): + coord = place.get_coord() + location = {"lat": coord["lat"], "lng": coord["lon"]} + if place.PLACE_TYPE != "latlon": + name = place.get_name(lang) + if name: + location["name"] = name + + if place.get_class_name() in ("railway", "bus"): + location["type"] = "publictransport" + + return location + def directions_combigo(self, start, end, mode, lang): if not self.COMBIGO_BASE_URL: raise HTTPException( - status_code=501, detail=f"Directions API is currently unavailable for mode {mode}", + status_code=501, detail=f"Directions API is currently unavailable for mode {mode}" ) - start_lon, start_lat = start - end_lon, end_lat = end - if "_" in lang: # Combigo does not handle long locale format lang = lang[: lang.find("_")] @@ -134,11 +155,11 @@ def directions_combigo(self, start, end, mode, lang): response = self.combigo_session.post( f"{self.COMBIGO_BASE_URL}/journey", - params={"lang": lang,}, + params={"lang": lang}, json={ "locations": [ - {"lat": start_lat, "lng": start_lon}, - {"lat": end_lat, "lng": end_lon}, + self.place_to_combigo_location(start, lang), + self.place_to_combigo_location(end, lang), ], "type_include": mode, "dTime": (datetime.utcnow() + timedelta(minutes=1)).isoformat(timespec="seconds"), @@ -148,10 +169,8 @@ def directions_combigo(self, start, end, mode, lang): response.raise_for_status() return DirectionsResponse(status="success", data=response.json()) - def get_directions( - self, from_loc, to_loc, mode, lang, params: QueryParams - ) -> DirectionsResponse: - if not DirectionsClient.is_in_allowed_zone(mode, from_loc, to_loc): + def get_directions(self, from_place, to_place, mode, lang, params: QueryParams): + if not DirectionsClient.is_in_allowed_zone(mode, from_place, to_place): raise HTTPException( status_code=422, detail="requested path is not inside an allowed area", ) @@ -172,9 +191,7 @@ def get_directions( kwargs = {} mode = mode else: - raise HTTPException( - status_code=400, detail=f"unknown mode {mode}", - ) + raise HTTPException(status_code=400, detail=f"unknown mode {mode}") method_name = method.__name__ logger.info( @@ -183,11 +200,11 @@ def get_directions( "method": method_name, "mode": mode, "lang": lang, - "from": from_loc, - "to": to_loc, + "from_place": from_place.get_id(), + "to_place": to_place.get_id(), }, ) - return method(from_loc, to_loc, mode, lang, **kwargs) + return method(from_place, to_place, mode, lang, **kwargs) directions_client = DirectionsClient() diff --git a/idunn/places/__init__.py b/idunn/places/__init__.py index 77fb470a1..d6e9c2743 100644 --- a/idunn/places/__init__.py +++ b/idunn/places/__init__.py @@ -6,3 +6,5 @@ from .pj_poi import PjPOI from .latlon import Latlon from .event import Event + +from .utils import place_from_id, InvalidPlaceId diff --git a/idunn/places/exceptions.py b/idunn/places/exceptions.py new file mode 100644 index 000000000..0f65bc8da --- /dev/null +++ b/idunn/places/exceptions.py @@ -0,0 +1,5 @@ +class InvalidPlaceId(ValueError): + def __init__(self, place_id): + self.id = place_id + self.message = f"Invalid place id: '{place_id}'" + super().__init__(self.message) diff --git a/idunn/places/latlon.py b/idunn/places/latlon.py index 7dc0e6619..8572ce92f 100644 --- a/idunn/places/latlon.py +++ b/idunn/places/latlon.py @@ -1,9 +1,11 @@ from .address import Address from .base import BasePlace +from .exceptions import InvalidPlaceId class Latlon(BasePlace): PLACE_TYPE = "latlon" + PLACE_ID_NAMESPACE = "latlon" def __init__(self, lat, lon, closest_address=None): self.lat = round(float(lat), 5) @@ -11,13 +13,21 @@ def __init__(self, lat, lon, closest_address=None): self.closest_address = closest_address or Address({}) super().__init__(self.closest_address) + @classmethod + def from_id(cls, id): + try: + namespace, lat, lon = id.split(":") + except ValueError: + raise InvalidPlaceId(id) + return cls(lat, lon) + def build_address(self, lang): if self.closest_address: return self.closest_address.build_address(lang) return None def get_id(self): - return f"latlon:{self.lat:.5f}:{self.lon:.5f}" + return f"{self.PLACE_ID_NAMESPACE}:{self.lat:.5f}:{self.lon:.5f}" def get_local_name(self): return f"{self.lat:.5f} : {self.lon:.5f}" diff --git a/idunn/places/place.py b/idunn/places/place.py index e607bdb68..2c18f50f8 100644 --- a/idunn/places/place.py +++ b/idunn/places/place.py @@ -1,7 +1,7 @@ from idunn.blocks.base import BaseBlock, BlocksValidator from idunn.api.utils import LONG, BLOCKS_BY_VERBOSITY from pydantic import BaseModel -from typing import ClassVar, List, Optional +from typing import List, Optional class PlaceMeta(BaseModel): diff --git a/idunn/places/utils.py b/idunn/places/utils.py new file mode 100644 index 000000000..42f907ba0 --- /dev/null +++ b/idunn/places/utils.py @@ -0,0 +1,49 @@ +from idunn.api.pages_jaunes import pj_source +from idunn.utils.es_wrapper import get_elasticsearch +from idunn.api.utils import fetch_es_place +from idunn.utils import prometheus + +from .admin import Admin +from .street import Street +from .address import Address +from .poi import POI +from .latlon import Latlon +from .exceptions import InvalidPlaceId + + +def place_from_id(id, type=None): + """ + :param id: place id + :param type: Optional type to restrict query in Elasticsearch + :return: Place + """ + try: + namespace, _ = id.split(":", 1) + except ValueError: + raise InvalidPlaceId(id) + + # Handle place from "pages jaunes" + if namespace == pj_source.PLACE_ID_NAMESPACE: + return pj_source.get_place(id) + + # Simple latlon place id + if namespace == Latlon.PLACE_ID_NAMESPACE: + return Latlon.from_id(id) + + #  Otherwise handle places from the ES db + es = get_elasticsearch() + es_place = fetch_es_place(id, es, type) + places = { + "admin": Admin, + "street": Street, + "addr": Address, + "poi": POI, + } + loader = places.get(es_place.get("_type")) + + if loader is None: + prometheus.exception("FoundPlaceWithWrongType") + raise Exception( + "Place with id '{}' has a wrong type: '{}'".format(id, es_place[0].get("_type")) + ) + return loader(es_place["_source"]) diff --git a/idunn/utils/default_settings.yaml b/idunn/utils/default_settings.yaml index 48842dbfc..2219b8caa 100644 --- a/idunn/utils/default_settings.yaml +++ b/idunn/utils/default_settings.yaml @@ -37,8 +37,8 @@ PROMETHEUS_MULTIPROC: False PLACE_ADMIN_INDEX: "munin_admin" PLACE_STREET_INDEX: "munin_street" PLACE_ADDRESS_INDEX: "munin_addr" -PLACE_POI_INDEX: "munin_poi" -PLACE_DEFAULT_INDEX: "munin" +PLACE_POI_INDEX: "munin_poi,munin_poi_nosearch" +PLACE_DEFAULT_INDEX: "munin,munin_poi_nosearch" WIKI_DESC_MAX_SIZE: 325 # max size allowed to the description of the wiki block diff --git a/tests/fixtures/street_birnenweg.json b/tests/fixtures/street_birnenweg.json index e75156966..5db5b8c16 100644 --- a/tests/fixtures/street_birnenweg.json +++ b/tests/fixtures/street_birnenweg.json @@ -89,7 +89,7 @@ "lat": 53.847809999999996, "lon": 10.6646915 }, - "id": "35460343", + "id": "street:35460343", "label": "Birnenweg (Label)", "name": "Birnenweg", "street_name": "Birnenweg", diff --git a/tests/test_cache.py b/tests/test_cache.py index 4e0227c11..9f6550aa7 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -63,7 +63,6 @@ def test_wikidata_cache(cache_test_normal, basket_ball_wiki_es, monkeypatch): """ client = TestClient(app) - # TODO: failing because of 404 with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: """ We mock all wikipedia requests since diff --git a/tests/test_directions.py b/tests/test_directions.py index af29afb8b..f6af1ef5d 100644 --- a/tests/test_directions.py +++ b/tests/test_directions.py @@ -76,6 +76,30 @@ def test_direction_car(mock_directions_car): assert "exclude=ferry" in mock_directions_car.calls[0].request.url +def test_direction_car_with_ids(mock_directions_car): + client = TestClient(app) + response = client.get( + "http://localhost/v1/directions", + params={ + "language": "fr", + "type": "driving", + "exclude": "ferry", + "origin": "latlon:48.89007:2.34023", + "destination": "osm:way:63178753", + }, + ) + + assert response.status_code == 200 + + response_data = response.json() + assert response_data["status"] == "success" + assert len(response_data["data"]["routes"]) == 3 + assert response_data["data"]["routes"][0]["legs"][0]["mode"] == "CAR" + mocked_request_url = mock_directions_car.calls[0].request.url + assert "exclude=ferry" in mocked_request_url + assert "2.34023,48.89007;2.32658,48.85992" in mocked_request_url + + def test_direction_public_transport(mock_directions_public_transport): client = TestClient(app) response = client.get( diff --git a/tests/test_opening_hours.py b/tests/test_opening_hours.py index c65570c09..664ba465b 100644 --- a/tests/test_opening_hours.py +++ b/tests/test_opening_hours.py @@ -13,8 +13,6 @@ for each we create a fake different OpeningHourBlock from a raw json extracted from a POI located in Moscow city. -TODO: a test that checks that opening hours can now span -over midnight (branch 'new-parsing' of the repo) """ diff --git a/tests/test_pj_poi.py b/tests/test_pj_poi.py index b7fee2ccc..2b91c8d7b 100644 --- a/tests/test_pj_poi.py +++ b/tests/test_pj_poi.py @@ -4,7 +4,7 @@ import os from app import app -from idunn.api import places +from idunn.places import utils as places_utils from .utils import enable_pj_source @@ -18,10 +18,10 @@ def read_fixture(filename): def test_pj_place(): musee_picasso = read_fixture("musee_picasso.json") with mock.patch.object( - places.pj_source.es, "search", new=lambda *x, **y: {"hits": {"hits": [musee_picasso]}} + places_utils.pj_source.es, "search", new=lambda *x, **y: {"hits": {"hits": [musee_picasso]}} ): client = TestClient(app) - response = client.get(url=f"http://localhost/v1/places/pj:05360257?lang=fr",) + response = client.get(url=f"http://localhost/v1/places/pj:05360257?lang=fr") assert response.status_code == 200 resp = response.json() @@ -60,10 +60,12 @@ def test_pj_place(): def test_pj_place_with_missing_data(): musee_picasso_short = read_fixture("musee_picasso_short.json") with mock.patch.object( - places.pj_source.es, "search", new=lambda *x, **y: {"hits": {"hits": [musee_picasso_short]}} + places_utils.pj_source.es, + "search", + new=lambda *x, **y: {"hits": {"hits": [musee_picasso_short]}}, ): client = TestClient(app) - response = client.get(url=f"http://localhost/v1/places/pj:05360257?lang=fr",) + response = client.get(url=f"http://localhost/v1/places/pj:05360257?lang=fr") assert response.status_code == 200 resp = response.json() diff --git a/tests/test_places.py b/tests/test_places.py index 0d868d08a..b9366a666 100644 --- a/tests/test_places.py +++ b/tests/test_places.py @@ -57,7 +57,7 @@ def test_full_query_street(): Test the response format to a street query """ client = TestClient(app) - response = client.get(url=f"http://localhost/v1/places/35460343?lang=fr",) + response = client.get(url=f"http://localhost/v1/places/street:35460343?lang=fr",) assert response.status_code == 200 assert response.headers.get("Access-Control-Allow-Origin") == "*" @@ -66,7 +66,7 @@ def test_full_query_street(): assert resp == { "type": "street", - "id": "35460343", + "id": "street:35460343", "name": "Birnenweg", "local_name": "Birnenweg", "class_name": "street", @@ -84,7 +84,7 @@ def test_full_query_street(): "housenumber": None, "postcode": "77777", "street": { - "id": "35460343", + "id": "street:35460343", "name": "Birnenweg", "label": "Birnenweg (Label)", "postcodes": ["77777"], @@ -423,14 +423,14 @@ def test_admin_i18n_name(): def test_type_query_street(): client = TestClient(app) - response = client.get(url=f"http://localhost/v1/places/35460343?lang=fr&type=street",) + response = client.get(url=f"http://localhost/v1/places/street:35460343?lang=fr&type=street",) assert response.status_code == 200 assert response.headers.get("Access-Control-Allow-Origin") == "*" resp = response.json() - assert resp["id"] == "35460343" + assert resp["id"] == "street:35460343" assert resp["name"] == "Birnenweg" @@ -485,9 +485,7 @@ def test_wrong_type(): response = client.get(url=f"http://localhost/v1/places/{id_moulin}?lang=fr&type=poi",) assert response.status_code == 404 - assert ( - response._content == b'{"detail":"place addr:5.108632;48.810273 not found with type=poi"}' - ) + assert response.json() == {"detail": "place 'addr:5.108632;48.810273' not found with type=poi"} def test_basic_short_query_poi(): diff --git a/tests/utils.py b/tests/utils.py index 303802c34..7b811cd52 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,10 +1,10 @@ from contextlib import contextmanager from copy import deepcopy -import pytest from idunn import settings -from idunn.api import places, places_list +from idunn.api import places_list from idunn.api.pages_jaunes import PjSource +from idunn.places import utils as places_utils @contextmanager @@ -22,15 +22,15 @@ def override_settings(overrides): @contextmanager def enable_pj_source(): - old_source = places.pj_source + old_source = places_list.pj_source with override_settings({"PJ_ES": "http://pj_es.test"}): new_source = PjSource() - places.pj_source = new_source + places_utils.pj_source = new_source places_list.pj_source = new_source try: yield finally: - places.pj_source = old_source + places_utils.pj_source = old_source places_list.pj_source = old_source