From a2fb77f6689c21b9bab195e77b9906fa7d0523c5 Mon Sep 17 00:00:00 2001 From: Vincent Sarago Date: Tue, 18 Feb 2025 11:30:07 -0500 Subject: [PATCH] use enum values and fix fields pydantic validation (#200) * use enum values and fix fields pydantic validation * revert --- CHANGES.md | 9 +++++++++ stac_fastapi/pgstac/core.py | 29 +++++++++++++++++++++++++---- stac_fastapi/pgstac/models/links.py | 8 ++++---- tests/resources/test_item.py | 7 +++++++ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 211e815..c42b02b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,15 @@ ## [Unreleased] +### Fixed + +- use Relation's `value` for `POST` prev/next links +- return `JSONResponse` directly from `/items` endpoint when `fields` parameter is pass and avoid Pydantic validation + +### Changed + +- avoid re-use of internal `CoreCrudClient.post_search` in `CoreCrudClient.get_search` method to allow customization + ## [4.0.1] - 2025-02-06 ### Added diff --git a/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/core.py index 559d8f0..71937a1 100644 --- a/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/core.py @@ -393,7 +393,13 @@ async def item_collection( sortby=sortby, ) - search_request = self.pgstac_search_model(**clean) + try: + search_request = self.pgstac_search_model(**clean) + except ValidationError as e: + raise HTTPException( + status_code=400, detail=f"Invalid parameters provided {e}" + ) from e + item_collection = await self._search_base(search_request, request=request) links = await ItemCollectionLinks( @@ -401,7 +407,14 @@ async def item_collection( ).get_links(extra_links=item_collection["links"]) item_collection["links"] = links - return item_collection + # If we have the `fields` extension enabled + # we need to avoid Pydantic validation because the + # Items might not be a valid STAC Item objects + if fields := getattr(search_request, "fields", None): + if fields.include or fields.exclude: + return JSONResponse(item_collection) # type: ignore + + return ItemCollection(**item_collection) async def get_item( self, item_id: str, collection_id: str, request: Request, **kwargs @@ -500,7 +513,6 @@ async def get_search( filter_lang=filter_lang, ) - # Do the request try: search_request = self.pgstac_search_model(**clean) except ValidationError as e: @@ -508,7 +520,16 @@ async def get_search( status_code=400, detail=f"Invalid parameters provided {e}" ) from e - return await self.post_search(search_request, request=request) + item_collection = await self._search_base(search_request, request=request) + + # If we have the `fields` extension enabled + # we need to avoid Pydantic validation because the + # Items might not be a valid STAC Item objects + if fields := getattr(search_request, "fields", None): + if fields.include or fields.exclude: + return JSONResponse(item_collection) # type: ignore + + return ItemCollection(**item_collection) def _clean_search_args( # noqa: C901 self, diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py index c0ec445..819e39e 100644 --- a/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/models/links.py @@ -140,8 +140,8 @@ def link_next(self) -> Optional[Dict[str, Any]]: if method == "POST": return { - "rel": Relations.next, - "type": MimeTypes.geojson, + "rel": Relations.next.value, + "type": MimeTypes.geojson.value, "method": method, "href": f"{self.request.url}", "body": {**self.request.postbody, "token": f"next:{self.next}"}, @@ -164,8 +164,8 @@ def link_prev(self) -> Optional[Dict[str, Any]]: if method == "POST": return { - "rel": Relations.previous, - "type": MimeTypes.geojson, + "rel": Relations.previous.value, + "type": MimeTypes.geojson.value, "method": method, "href": f"{self.request.url}", "body": {**self.request.postbody, "token": f"prev:{self.prev}"}, diff --git a/tests/resources/test_item.py b/tests/resources/test_item.py index 6141369..afba240 100644 --- a/tests/resources/test_item.py +++ b/tests/resources/test_item.py @@ -1072,6 +1072,13 @@ async def test_field_extension_get(app_client, load_test_data, load_test_collect ) assert resp.status_code == 201 + params = {"fields": "+properties.proj:epsg,+properties.gsd,+collection"} + resp = await app_client.get( + f"/collections/{test_item['collection']}/items", params=params + ) + feat_properties = resp.json()["features"][0]["properties"] + assert not set(feat_properties) - {"proj:epsg", "gsd", "datetime"} + params = {"fields": "+properties.proj:epsg,+properties.gsd,+collection"} resp = await app_client.get("/search", params=params) feat_properties = resp.json()["features"][0]["properties"]