From a915f2ac5cfddeb41885f348f4d9516d3d53344a Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Thu, 27 Nov 2025 22:31:36 -0800 Subject: [PATCH 01/10] Add test for collections fields extension --- tests/api/test_api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 0c9057dc..2fdf80a1 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -271,6 +271,18 @@ async def test_app_query_extension_gte(load_test_data, app_client, load_test_col assert len(resp_json["features"]) == 1 +async def test_app_collection_fields_extension( + load_test_data, app_client, load_test_collection +): + fields = ["id", "title"] + resp = await app_client.get("/collections", params={"fields": ",".join(fields)}) + assert resp.status_code == 200 + resp_json = resp.json() + resp_collections = resp_json["collections"] + assert len(resp_collections) > 0 + assert all(set(collection.keys()) == set(fields) for collection in resp_collections) + + async def test_app_sort_extension(load_test_data, app_client, load_test_collection): coll = load_test_collection first_item = load_test_data("test_item.json") From 51d2cfe2d5033a529b692fc8e8d427f09e1e23b3 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 10:22:33 -0800 Subject: [PATCH 02/10] fix: convert set to list --- stac_fastapi/pgstac/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/core.py index 3d01fb7a..fe8d6740 100644 --- a/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/core.py @@ -617,7 +617,7 @@ def _clean_search_args( # noqa: C901 else: includes.add(field) - base_args["fields"] = {"include": includes, "exclude": excludes} + base_args["fields"] = {"include": list(includes), "exclude": list(excludes)} if q: base_args["q"] = q From 761036d9ebdd58dbf51795bd83405c1a85940cc7 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 11:10:54 -0800 Subject: [PATCH 03/10] Accelerate tests temporarily --- tests/conftest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f4b42f39..1bf41249 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -68,7 +68,7 @@ def database(postgresql_proc): @pytest.fixture( params=[ - "0.8.6", + # "0.8.6", "0.9.8", ], ) @@ -96,11 +96,11 @@ def pgstac(request, database): params=[ # API hydratation, prefix, model_validation (False, "", False), - (False, "/router_prefix", False), - (True, "", False), - (True, "/router_prefix", False), - (False, "", True), - (True, "", True), + # (False, "/router_prefix", False), + # (True, "", False), + # (True, "/router_prefix", False), + # (False, "", True), + # (True, "", True), ], scope="session", ) From 76c0a9a291fb1984c2bd08407c2bd29367b93321 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 11:17:35 -0800 Subject: [PATCH 04/10] Maybe fix test? --- tests/api/test_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 2fdf80a1..9005a188 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -280,7 +280,8 @@ async def test_app_collection_fields_extension( resp_json = resp.json() resp_collections = resp_json["collections"] assert len(resp_collections) > 0 - assert all(set(collection.keys()) == set(fields) for collection in resp_collections) + for collection in resp_collections: + assert set(collection.keys()) == set(fields + ["links", "collection"]) async def test_app_sort_extension(load_test_data, app_client, load_test_collection): From 04600a3364e18706715874c8b9de26d7df505ae8 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 12:45:27 -0800 Subject: [PATCH 05/10] Finalize test --- tests/api/test_api.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 9005a188..7304572e 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -272,16 +272,25 @@ async def test_app_query_extension_gte(load_test_data, app_client, load_test_col async def test_app_collection_fields_extension( - load_test_data, app_client, load_test_collection + load_test_data, app_client, load_test_collection, app ): - fields = ["id", "title"] + if app.state.settings.enable_response_models: + # https://github.com/stac-utils/stac-fastapi-pgstac/issues/328 + pytest.skip("Skipping test when model_validation is enabled, see #328") + + fields = ["title"] resp = await app_client.get("/collections", params={"fields": ",".join(fields)}) + assert resp.status_code == 200 + resp_json = resp.json() resp_collections = resp_json["collections"] + assert len(resp_collections) > 0 + # NOTE: It's a bug that 'collection' is always included; see #327 + constant_fields = ["id", "links", "collection"] for collection in resp_collections: - assert set(collection.keys()) == set(fields + ["links", "collection"]) + assert set(collection.keys()) == set(fields + constant_fields) async def test_app_sort_extension(load_test_data, app_client, load_test_collection): From 06733b3ef58be4492b3d50f9b3c574f65613dedd Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 14:23:01 -0800 Subject: [PATCH 06/10] Add items tests --- tests/api/test_api.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 7304572e..49a9c389 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -293,6 +293,27 @@ async def test_app_collection_fields_extension( assert set(collection.keys()) == set(fields + constant_fields) +async def test_app_item_fields_extension( + load_test_data, app_client, load_test_collection, load_test_item, app +): + coll = load_test_collection + fields = ["id", "geometry"] + resp = await app_client.get( + f"/collections/{coll['id']}/items", params={"fields": ",".join(fields)} + ) + + assert resp.status_code == 200 + + resp_json = resp.json() + features = resp_json["features"] + + assert len(features) > 0 + # These fields are always included in items + constant_fields = ["type", "links", "assets", "collection", "stac_version"] + for item in features: + assert set(item.keys()) == set(fields + constant_fields) + + async def test_app_sort_extension(load_test_data, app_client, load_test_collection): coll = load_test_collection first_item = load_test_data("test_item.json") From 15dac9302597e66de0c90f4c290a7b59eb67c9d4 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 17:21:26 -0800 Subject: [PATCH 07/10] fix: update constant fields in item fields extension test --- tests/api/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 49a9c389..eb27236b 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -309,7 +309,7 @@ async def test_app_item_fields_extension( assert len(features) > 0 # These fields are always included in items - constant_fields = ["type", "links", "assets", "collection", "stac_version"] + constant_fields = ["id", "collection", "links"] for item in features: assert set(item.keys()) == set(fields + constant_fields) From 4452922352105c295f1f6f3d9b0a5b7ff921f445 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 17:27:41 -0800 Subject: [PATCH 08/10] Revert "Accelerate tests temporarily" This reverts commit 761036d9ebdd58dbf51795bd83405c1a85940cc7. --- tests/api/test_api.py | 2 +- tests/conftest.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index eb27236b..4ab55598 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -309,7 +309,7 @@ async def test_app_item_fields_extension( assert len(features) > 0 # These fields are always included in items - constant_fields = ["id", "collection", "links"] + constant_fields = ["id", "links"] for item in features: assert set(item.keys()) == set(fields + constant_fields) diff --git a/tests/conftest.py b/tests/conftest.py index 1bf41249..f4b42f39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -68,7 +68,7 @@ def database(postgresql_proc): @pytest.fixture( params=[ - # "0.8.6", + "0.8.6", "0.9.8", ], ) @@ -96,11 +96,11 @@ def pgstac(request, database): params=[ # API hydratation, prefix, model_validation (False, "", False), - # (False, "/router_prefix", False), - # (True, "", False), - # (True, "/router_prefix", False), - # (False, "", True), - # (True, "", True), + (False, "/router_prefix", False), + (True, "", False), + (True, "/router_prefix", False), + (False, "", True), + (True, "", True), ], scope="session", ) From 7b3f48dbf7153de32a56e2ca8c71f47a2d023265 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Fri, 28 Nov 2025 18:20:25 -0800 Subject: [PATCH 09/10] fix: conditionally include 'collection' field in item fields extension test --- tests/api/test_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 4ab55598..730b750d 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -310,6 +310,8 @@ async def test_app_item_fields_extension( assert len(features) > 0 # These fields are always included in items constant_fields = ["id", "links"] + if not app.state.settings.use_api_hydrate: + constant_fields.append("collection") for item in features: assert set(item.keys()) == set(fields + constant_fields) From 4202c3b61be29aab1ca981ac28e4f07da12e6d62 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Mon, 1 Dec 2025 17:10:20 +0100 Subject: [PATCH 10/10] add passthrough for collections when fields is enabled --- stac_fastapi/pgstac/core.py | 10 +++++++++- tests/api/test_api.py | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/core.py index fe8d6740..626897a3 100644 --- a/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/core.py @@ -150,7 +150,7 @@ async def all_collections( # noqa: C901 prev=prev_link, ).get_links() - return Collections( + collections = Collections( collections=linked_collections or [], links=links, numberMatched=collections_result.get( @@ -161,6 +161,14 @@ async def all_collections( # noqa: C901 ), ) + # 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: + return JSONResponse(collections) # type: ignore + + return collections + async def get_collection( self, collection_id: str, diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 730b750d..7aa33d5f 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -274,10 +274,6 @@ async def test_app_query_extension_gte(load_test_data, app_client, load_test_col async def test_app_collection_fields_extension( load_test_data, app_client, load_test_collection, app ): - if app.state.settings.enable_response_models: - # https://github.com/stac-utils/stac-fastapi-pgstac/issues/328 - pytest.skip("Skipping test when model_validation is enabled, see #328") - fields = ["title"] resp = await app_client.get("/collections", params={"fields": ",".join(fields)})