Skip to content

Commit d0962d1

Browse files
committed
add fields extension, tests
1 parent 7d6b741 commit d0962d1

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

stac_fastapi/core/stac_fastapi/core/core.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,13 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:
225225
return landing_page
226226

227227
async def all_collections(
228-
self, sortby: Optional[str] = None, **kwargs
228+
self, fields: Optional[List[str]] = None, sortby: Optional[str] = None, **kwargs
229229
) -> stac_types.Collections:
230230
"""Read all collections from the database.
231231
232232
Args:
233+
fields (Optional[List[str]]): Fields to include or exclude from the results.
234+
sortby (Optional[str]): Sorting options for the results.
233235
**kwargs: Keyword arguments from the request.
234236
235237
Returns:
@@ -240,6 +242,15 @@ async def all_collections(
240242
limit = int(request.query_params.get("limit", os.getenv("STAC_ITEM_LIMIT", 10)))
241243
token = request.query_params.get("token")
242244

245+
# Process fields parameter for filtering collection properties
246+
includes, excludes = set(), set()
247+
if fields and self.extension_is_enabled("FieldsExtension"):
248+
for field in fields:
249+
if field[0] == "-":
250+
excludes.add(field[1:])
251+
else:
252+
includes.add(field[1:] if field[0] in "+ " else field)
253+
243254
sort = None
244255
if sortby:
245256
parsed_sort = []
@@ -259,6 +270,15 @@ async def all_collections(
259270
token=token, limit=limit, request=request, sort=sort
260271
)
261272

273+
# Apply field filtering if fields parameter was provided
274+
if fields and self.extension_is_enabled("FieldsExtension"):
275+
filtered_collections = [
276+
filter_fields(collection, includes, excludes)
277+
for collection in collections
278+
]
279+
else:
280+
filtered_collections = collections
281+
262282
links = [
263283
{"rel": Relations.root.value, "type": MimeTypes.json, "href": base_url},
264284
{"rel": Relations.parent.value, "type": MimeTypes.json, "href": base_url},
@@ -273,7 +293,7 @@ async def all_collections(
273293
next_link = PagingLinks(next=next_token, request=request).link_next()
274294
links.append(next_link)
275295

276-
return stac_types.Collections(collections=collections, links=links)
296+
return stac_types.Collections(collections=filtered_collections, links=links)
277297

278298
async def get_collection(
279299
self, collection_id: str, **kwargs

stac_fastapi/tests/api/test_api_search_collections.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,78 @@ async def test_collections_sort_id_desc(app_client, txn_client, load_test_data):
7777
assert len(test_collections) == len(collection_ids)
7878
for i, expected_id in enumerate(sorted_ids):
7979
assert test_collections[i]["id"] == expected_id
80+
81+
82+
@pytest.mark.asyncio
83+
async def test_collections_fields(app_client, txn_client, load_test_data):
84+
"""Verify GET /collections honors the fields parameter."""
85+
# Create multiple collections with different ids
86+
base_collection = load_test_data("test_collection.json")
87+
88+
# Create collections with ids in a specific order to test fields
89+
# Use unique prefixes to avoid conflicts between tests
90+
test_prefix = f"fields-{uuid.uuid4().hex[:8]}"
91+
collection_ids = [f"{test_prefix}-a", f"{test_prefix}-b", f"{test_prefix}-c"]
92+
93+
for i, coll_id in enumerate(collection_ids):
94+
test_collection = base_collection.copy()
95+
test_collection["id"] = coll_id
96+
test_collection["title"] = f"Test Collection {i}"
97+
test_collection["description"] = f"Description for collection {i}"
98+
await create_collection(txn_client, test_collection)
99+
100+
# Test include fields parameter
101+
resp = await app_client.get(
102+
"/collections",
103+
params=[("fields", "id"), ("fields", "title")],
104+
)
105+
assert resp.status_code == 200
106+
resp_json = resp.json()
107+
108+
# Check if collections exist in the response
109+
assert "collections" in resp_json, "No collections in response"
110+
111+
# Filter collections to only include the ones we created for this test
112+
test_collections = []
113+
for c in resp_json["collections"]:
114+
if "id" in c and c["id"].startswith(test_prefix):
115+
test_collections.append(c)
116+
117+
# Filter collections to only include the ones we created for this test
118+
test_collections = []
119+
for c in resp_json["collections"]:
120+
if "id" in c and c["id"].startswith(test_prefix):
121+
test_collections.append(c)
122+
123+
# Collections should only have id and title fields
124+
for collection in test_collections:
125+
assert "id" in collection
126+
assert "title" in collection
127+
assert "description" not in collection
128+
assert "links" in collection # links are always included
129+
130+
# Test exclude fields parameter
131+
resp = await app_client.get(
132+
"/collections",
133+
params=[("fields", "-description")],
134+
)
135+
assert resp.status_code == 200
136+
resp_json = resp.json()
137+
138+
# Check if collections exist in the response
139+
assert (
140+
"collections" in resp_json
141+
), "No collections in response for exclude fields test"
142+
143+
# Filter collections to only include the ones we created for this test
144+
test_collections = []
145+
for c in resp_json["collections"]:
146+
if "id" in c and c["id"].startswith(test_prefix):
147+
test_collections.append(c)
148+
149+
# Collections should have all fields except description
150+
for collection in test_collections:
151+
assert "id" in collection
152+
assert "title" in collection
153+
assert "description" not in collection
154+
assert "links" in collection

0 commit comments

Comments
 (0)