Skip to content

Commit 1f36485

Browse files
Enable search on /items and add queryables links (#89)
* Enable search on /items and add queryables links * add tests and remove unused * fix * more tests and update changelog --------- Co-authored-by: vincentsarago <vincent.sarago@gmail.com>
1 parent 7bfb9ce commit 1f36485

File tree

6 files changed

+71
-9
lines changed

6 files changed

+71
-9
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## [Unreleased]
44

5+
- Enable filter extension for `GET /items` requests and add `Queryables` links in `/collections` and `/collections/{collection_id}` responses ([#89](https://github.com/stac-utils/stac-fastapi-pgstac/pull/89))
6+
57
## [3.0.0a4] - 2024-07-10
68

79
- Update stac-fastapi libraries to `~=3.0.0b2`

stac_fastapi/pgstac/core.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ async def all_collections(self, request: Request, **kwargs) -> Collections:
5757
collection_id=coll["id"], request=request
5858
).get_links(extra_links=coll.get("links"))
5959

60+
if self.extension_is_enabled("FilterExtension"):
61+
coll["links"].append(
62+
{
63+
"rel": Relations.queryables.value,
64+
"type": MimeTypes.jsonschema.value,
65+
"title": "Queryables",
66+
"href": urljoin(
67+
base_url, f"collections/{coll['id']}/queryables"
68+
),
69+
}
70+
)
71+
6072
linked_collections.append(coll)
6173

6274
links = [
@@ -109,6 +121,17 @@ async def get_collection(
109121
collection_id=collection_id, request=request
110122
).get_links(extra_links=collection.get("links"))
111123

124+
if self.extension_is_enabled("FilterExtension"):
125+
base_url = get_base_url(request)
126+
collection["links"].append(
127+
{
128+
"rel": Relations.queryables.value,
129+
"type": MimeTypes.jsonschema.value,
130+
"title": "Queryables",
131+
"href": urljoin(base_url, f"collections/{collection_id}/queryables"),
132+
}
133+
)
134+
112135
return Collection(**collection)
113136

114137
async def _get_base_item(
@@ -285,6 +308,14 @@ async def item_collection(
285308
"token": token,
286309
}
287310

311+
if self.extension_is_enabled("FilterExtension"):
312+
filter_lang = kwargs.get("filter_lang", None)
313+
filter = kwargs.get("filter", None)
314+
if filter is not None and filter_lang == "cql2-text":
315+
ast = parse_cql2_text(filter.strip())
316+
base_args["filter"] = orjson.loads(to_cql2(ast))
317+
base_args["filter-lang"] = "cql2-json"
318+
288319
clean = {}
289320
for k, v in base_args.items():
290321
if v is not None and v != []:
@@ -377,14 +408,6 @@ async def get_search( # noqa: C901
377408
Returns:
378409
ItemCollection containing items which match the search criteria.
379410
"""
380-
query_params = str(request.query_params)
381-
382-
# Kludgy fix because using factory does not allow alias for filter-lang
383-
if filter_lang is None:
384-
match = re.search(r"filter-lang=([a-z0-9-]+)", query_params, re.IGNORECASE)
385-
if match:
386-
filter_lang = match.group(1)
387-
388411
# Parse request parameters
389412
base_args = {
390413
"collections": collections,

tests/api/test_api.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ async def test_get_search_content_type(app_client):
7373
assert resp.headers["content-type"] == "application/geo+json"
7474

7575

76+
async def test_landing_links(app_client):
77+
"""test landing page links."""
78+
landing = await app_client.get("/")
79+
assert landing.status_code == 200, landing.text
80+
assert "Queryables" in [link.get("title") for link in landing.json()["links"]]
81+
82+
7683
async def test_get_queryables_content_type(app_client, load_test_collection):
7784
resp = await app_client.get("queryables")
7885
assert resp.headers["content-type"] == "application/schema+json"
@@ -743,6 +750,9 @@ async def test_no_extension(
743750
async with AsyncClient(transport=ASGITransport(app=app)) as client:
744751
landing = await client.get("http://test/")
745752
assert landing.status_code == 200, landing.text
753+
assert "Queryables" not in [
754+
link.get("title") for link in landing.json()["links"]
755+
]
746756

747757
collection = await client.get("http://test/collections/test-collection")
748758
assert collection.status_code == 200, collection.text

tests/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ def api_client(request, database):
137137
items_get_request_model = create_request_model(
138138
model_name="ItemCollectionUri",
139139
base_model=ItemCollectionUri,
140-
mixins=[TokenPaginationExtension().GET],
140+
mixins=[
141+
TokenPaginationExtension().GET,
142+
FilterExtension(client=FiltersClient()).GET,
143+
],
141144
request_type="GET",
142145
)
143146
search_get_request_model = create_get_request_model(extensions)

tests/resources/test_collection.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,19 @@ async def test_get_collections_forwarded_header(app_client, load_test_collection
260260
)
261261
for link in resp.json()["links"]:
262262
assert link["href"].startswith("https://test:1234/")
263+
264+
265+
@pytest.mark.asyncio
266+
async def test_get_collections_queryables_links(app_client, load_test_collection):
267+
resp = await app_client.get(
268+
"/collections",
269+
)
270+
assert "Queryables" in [
271+
link.get("title") for link in resp.json()["collections"][0]["links"]
272+
]
273+
274+
collection_id = resp.json()["collections"][0]["id"]
275+
resp = await app_client.get(
276+
f"/collections/{collection_id}",
277+
)
278+
assert "Queryables" in [link.get("title") for link in resp.json()["links"]]

tests/resources/test_item.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,14 @@ async def test_get_filter_cql2text(app_client, load_test_data, load_test_collect
14711471
resp_json = resp.json()
14721472
assert len(resp.json()["features"]) == 0
14731473

1474+
filter = f"proj:epsg={epsg}"
1475+
params = {"filter": filter, "filter-lang": "cql2-text"}
1476+
resp = await app_client.get(
1477+
f"/collections/{test_item['collection']}/items", params=params
1478+
)
1479+
resp_json = resp.json()
1480+
assert len(resp.json()["features"]) == 1
1481+
14741482

14751483
async def test_item_merge_raster_bands(
14761484
app_client, load_test2_item, load_test2_collection

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy