Skip to content

Commit

Permalink
Update pgstac to return 400 on invalid date parameter.
Browse files Browse the repository at this point in the history
Also allow backends to raise their own InvalidQueryParameter
exceptions, which will be translated into a 400 by exception
handling. This is another option for backends that want to handle
query parameter validation in other ways besides stac-pydantic model validation.
  • Loading branch information
lossyrob committed Aug 19, 2021
1 parent 58c668f commit 05aed8c
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 8 deletions.
2 changes: 2 additions & 0 deletions stac_fastapi/api/stac_fastapi/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ConflictError,
DatabaseError,
ForeignKeyError,
InvalidQueryParameter,
NotFoundError,
)

Expand All @@ -25,6 +26,7 @@
ForeignKeyError: status.HTTP_422_UNPROCESSABLE_ENTITY,
DatabaseError: status.HTTP_424_FAILED_DEPENDENCY,
Exception: status.HTTP_500_INTERNAL_SERVER_ERROR,
InvalidQueryParameter: status.HTTP_400_BAD_REQUEST,
}


Expand Down
23 changes: 15 additions & 8 deletions stac_fastapi/pgstac/stac_fastapi/pgstac/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import attr
import orjson
from asyncpg.exceptions import InvalidDatetimeFormatError
from buildpg import render
from fastapi import HTTPException
from pydantic import ValidationError
Expand All @@ -16,7 +17,7 @@
from stac_fastapi.pgstac.models.links import CollectionLinks, ItemLinks, PagingLinks
from stac_fastapi.pgstac.types.search import PgstacSearch
from stac_fastapi.types.core import AsyncBaseCoreClient
from stac_fastapi.types.errors import NotFoundError
from stac_fastapi.types.errors import InvalidQueryParameter, NotFoundError
from stac_fastapi.types.stac import Collection, Collections, Item, ItemCollection

NumType = Union[float, int]
Expand Down Expand Up @@ -116,14 +117,20 @@ async def _search_base(
# pool = kwargs["request"].app.state.readpool
req = search_request.json(exclude_none=True)

async with pool.acquire() as conn:
q, p = render(
"""
SELECT * FROM search(:req::text::jsonb);
""",
req=req,
try:
async with pool.acquire() as conn:
q, p = render(
"""
SELECT * FROM search(:req::text::jsonb);
""",
req=req,
)
items = await conn.fetchval(q, *p)
except InvalidDatetimeFormatError:
raise InvalidQueryParameter(
f"Datetime parameter {search_request.datetime} is invalid."
)
items = await conn.fetchval(q, *p)

next: Optional[str] = items.pop("next", None)
prev: Optional[str] = items.pop("prev", None)
collection = ItemCollection(**items)
Expand Down
16 changes: 16 additions & 0 deletions stac_fastapi/pgstac/tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,19 @@ async def test_app_sort_extension(load_test_data, app_client, load_test_collecti
resp_json = resp.json()
assert resp_json["features"][1]["id"] == first_item["id"]
assert resp_json["features"][0]["id"] == second_item["id"]


@pytest.mark.asyncio
async def test_search_invalid_date(load_test_data, app_client, load_test_collection):
coll = load_test_collection
first_item = load_test_data("test_item.json")
resp = await app_client.post(f"/collections/{coll.id}/items", json=first_item)
assert resp.status_code == 200

params = {
"datetime": "2020-XX-01/2020-10-30",
"collections": [coll.id],
}

resp = await app_client.post("/search", json=params)
assert resp.status_code == 400
16 changes: 16 additions & 0 deletions stac_fastapi/sqlalchemy/tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,19 @@ def test_app_sort_extension(load_test_data, app_client, postgres_transactions):
resp_json = resp.json()
assert resp_json["features"][0]["id"] == first_item["id"]
assert resp_json["features"][1]["id"] == second_item["id"]


def test_search_invalid_date(load_test_data, app_client, postgres_transactions):
item = load_test_data("test_item.json")
postgres_transactions.create_item(item, request=MockStarletteRequest)

params = {
"datetime": "2020-XX-01/2020-10-30",
"collections": [item["collection"]],
}

resp = app_client.post("/search", json=params)
import json

print(json.dumps(resp.json(), indent=2))
assert resp.status_code == 400
10 changes: 10 additions & 0 deletions stac_fastapi/types/stac_fastapi/types/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,13 @@ class DatabaseError(StacApiError):
"""Generic database errors."""

pass


class InvalidQueryParameter(StacApiError):
"""Error for unknown or invalid query parameters.
Used to capture errors that should respond according to
http://docs.opengeospatial.org/is/17-069r3/17-069r3.html#query_parameters
"""

pass

0 comments on commit 05aed8c

Please sign in to comment.