From 3e5fc1c74112e842bbac20de8f70ed71f01131be Mon Sep 17 00:00:00 2001 From: Vincent Sarago Date: Thu, 25 Apr 2024 13:05:15 +0200 Subject: [PATCH] fix AsyncBaseCoreClient urls (#675) --- CHANGES.md | 6 ++ stac_fastapi/api/tests/conftest.py | 58 ++++++++++++++ stac_fastapi/api/tests/test_app_prefix.py | 80 +++++++++++++++++++ stac_fastapi/types/stac_fastapi/types/core.py | 4 +- 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 77a7f2c1..c0f76867 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [2.5.5.post1] - 2024-04-25 + +### Fixed + +* Fix `service-doc` and `service-desc` url in landing page when using router prefix for `AsyncBaseCoreClient` + ## [2.5.5] - 2024-04-24 ### Fixed diff --git a/stac_fastapi/api/tests/conftest.py b/stac_fastapi/api/tests/conftest.py index 01db30ec..ed8c66d4 100644 --- a/stac_fastapi/api/tests/conftest.py +++ b/stac_fastapi/api/tests/conftest.py @@ -116,3 +116,61 @@ def item_collection( ) return CoreClient + + +@pytest.fixture +def AsyncTestCoreClient(collection_dict, item_dict): + class AsyncCoreClient(core.AsyncBaseCoreClient): + async def post_search( + self, search_request: BaseSearchPostRequest, **kwargs + ) -> stac.ItemCollection: + return stac.ItemCollection( + type="FeatureCollection", features=[stac.Item(**item_dict)] + ) + + async def get_search( + self, + collections: Optional[List[str]] = None, + ids: Optional[List[str]] = None, + bbox: Optional[List[NumType]] = None, + intersects: Optional[str] = None, + datetime: Optional[Union[str, datetime]] = None, + limit: Optional[int] = 10, + **kwargs, + ) -> stac.ItemCollection: + return stac.ItemCollection( + type="FeatureCollection", features=[stac.Item(**item_dict)] + ) + + async def get_item( + self, item_id: str, collection_id: str, **kwargs + ) -> stac.Item: + return stac.Item(**item_dict) + + async def all_collections(self, **kwargs) -> stac.Collections: + return stac.Collections( + collections=[stac.Collection(**collection_dict)], + links=[ + {"href": "test", "rel": "root"}, + {"href": "test", "rel": "self"}, + {"href": "test", "rel": "parent"}, + ], + ) + + async def get_collection(self, collection_id: str, **kwargs) -> stac.Collection: + return stac.Collection(**collection_dict) + + async def item_collection( + self, + collection_id: str, + bbox: Optional[List[Union[float, int]]] = None, + datetime: Optional[Union[str, datetime]] = None, + limit: int = 10, + token: str = None, + **kwargs, + ) -> stac.ItemCollection: + return stac.ItemCollection( + type="FeatureCollection", features=[stac.Item(**item_dict)] + ) + + return AsyncCoreClient diff --git a/stac_fastapi/api/tests/test_app_prefix.py b/stac_fastapi/api/tests/test_app_prefix.py index 39f099c5..0e76e456 100644 --- a/stac_fastapi/api/tests/test_app_prefix.py +++ b/stac_fastapi/api/tests/test_app_prefix.py @@ -46,9 +46,89 @@ def test_api_prefix(TestCoreClient, prefix): conformance = client.get(f"{prefix}/conformance") assert conformance.status_code == 200, conformance.json() + # NOTE: The collections/collection/items/item links do not have the prefix + # because they are created in the fixtures + collections = client.get(f"{prefix}/collections") + assert collections.status_code == 200, collections.json() + collection_id = collections.json()["collections"][0]["id"] + print(collections.json()["links"]) + collection = client.get(f"{prefix}/collections/{collection_id}") + assert collection.status_code == 200, collection.json() + + items = client.get(f"{prefix}/collections/{collection_id}/items") + assert items.status_code == 200, items.json() + + item_id = items.json()["features"][0]["id"] + item = client.get(f"{prefix}/collections/{collection_id}/items/{item_id}") + assert item.status_code == 200, item.json() + + link_tests = [ + ("root", "application/json", "/"), + ("conformance", "application/json", "/conformance"), + ("data", "application/json", "/collections"), + ("search", "application/geo+json", "/search"), + ("service-doc", "text/html", "/api.html"), + ("service-desc", "application/vnd.oai.openapi+json;version=3.0", "/api"), + ] + + for rel_type, expected_media_type, expected_path in link_tests: + link = get_link(landing.json(), rel_type) + + assert link is not None, f"Missing {rel_type} link in landing page" + assert link.get("type") == expected_media_type + + link_path = urllib.parse.urlsplit(link.get("href")).path + assert link_path == prefix + expected_path + + resp = client.get(prefix + expected_path) + assert resp.status_code == 200 + + +@pytest.mark.parametrize("prefix", ["", "/a_prefix"]) +def test_async_api_prefix(AsyncTestCoreClient, prefix): + api_settings = ApiSettings( + openapi_url=f"{prefix}/api", + docs_url=f"{prefix}/api.html", + ) + + api = StacApi( + settings=api_settings, + client=AsyncTestCoreClient(), + router=APIRouter(prefix=prefix), + ) + + with TestClient(api.app, base_url="http://stac.io") as client: + landing = client.get(f"{prefix}/") + assert landing.status_code == 200, landing.json() + + service_doc = client.get(f"{prefix}/api.html") + assert service_doc.status_code == 200, service_doc.text + + service_desc = client.get(f"{prefix}/api") + assert service_desc.status_code == 200, service_desc.json() + + conformance = client.get(f"{prefix}/conformance") + assert conformance.status_code == 200, conformance.json() + + collections = client.get(f"{prefix}/collections") + assert collections.status_code == 200, collections.json() + collection_id = collections.json()["collections"][0]["id"] + + collection = client.get(f"{prefix}/collections/{collection_id}") + assert collection.status_code == 200, collection.json() + + items = client.get(f"{prefix}/collections/{collection_id}/items") + assert items.status_code == 200, items.json() + + item_id = items.json()["features"][0]["id"] + item = client.get(f"{prefix}/collections/{collection_id}/items/{item_id}") + assert item.status_code == 200, item.json() + link_tests = [ ("root", "application/json", "/"), ("conformance", "application/json", "/conformance"), + ("data", "application/json", "/collections"), + ("search", "application/geo+json", "/search"), ("service-doc", "text/html", "/api.html"), ("service-desc", "application/vnd.oai.openapi+json;version=3.0", "/api"), ] diff --git a/stac_fastapi/types/stac_fastapi/types/core.py b/stac_fastapi/types/stac_fastapi/types/core.py index c6227a57..05c3e109 100644 --- a/stac_fastapi/types/stac_fastapi/types/core.py +++ b/stac_fastapi/types/stac_fastapi/types/core.py @@ -612,7 +612,7 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage: "rel": "service-desc", "type": "application/vnd.oai.openapi+json;version=3.0", "title": "OpenAPI service description", - "href": urljoin(base_url, request.app.openapi_url.lstrip("/")), + "href": str(request.url_for("openapi")), } ) @@ -622,7 +622,7 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage: "rel": "service-doc", "type": "text/html", "title": "OpenAPI service documentation", - "href": urljoin(base_url, request.app.docs_url.lstrip("/")), + "href": str(request.url_for("swagger_ui_html")), } )