Skip to content

Commit

Permalink
keep datetime as string type in GET Request models (#780)
Browse files Browse the repository at this point in the history
* keep datetime as string type in GET Request models

* fix

* use mixin

* reorder

* add parse_datetime
  • Loading branch information
vincentsarago authored Jan 17, 2025
1 parent 6d65525 commit e3c138c
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 56 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

## Changed

* use `string` type instead of python `datetime.datetime` for datetime parameter in `BaseSearchGetRequest`, `ItemCollectionUri` and `BaseCollectionSearchGetRequest` GET models

## [3.0.5] - 2025-01-10

### Removed
Expand Down
11 changes: 5 additions & 6 deletions stac_fastapi/api/stac_fastapi/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
from typing_extensions import Annotated

from stac_fastapi.types.extension import ApiExtension
from stac_fastapi.types.rfc3339 import DateTimeType
from stac_fastapi.types.search import (
APIRequest,
BaseSearchGetRequest,
BaseSearchPostRequest,
DatetimeMixin,
DateTimeQueryType,
Limit,
_bbox_converter,
_datetime_converter,
_validate_datetime,
)

try:
Expand Down Expand Up @@ -110,7 +111,7 @@ class EmptyRequest(APIRequest):


@attr.s
class ItemCollectionUri(APIRequest):
class ItemCollectionUri(APIRequest, DatetimeMixin):
"""Get item collection."""

collection_id: Annotated[str, Path(description="Collection ID")] = attr.ib()
Expand All @@ -121,9 +122,7 @@ class ItemCollectionUri(APIRequest):
),
] = attr.ib(default=10)
bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter)
datetime: Optional[DateTimeType] = attr.ib(
default=None, converter=_datetime_converter
)
datetime: DateTimeQueryType = attr.ib(default=None, validator=_validate_datetime)


class GeoJSONResponse(JSONResponse):
Expand Down
33 changes: 26 additions & 7 deletions stac_fastapi/api/tests/test_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from datetime import datetime
from typing import List, Optional, Union

import attr
Expand Down Expand Up @@ -141,7 +140,7 @@ def get_search(
ids: Optional[List[str]] = None,
bbox: Optional[List[NumType]] = None,
intersects: Optional[str] = None,
datetime: Optional[Union[str, datetime]] = None,
datetime: Optional[str] = None,
limit: Optional[int] = 10,
filter: Optional[str] = None,
filter_crs: Optional[str] = None,
Expand Down Expand Up @@ -221,7 +220,7 @@ def get_search(
ids: Optional[List[str]] = None,
bbox: Optional[List[NumType]] = None,
intersects: Optional[str] = None,
datetime: Optional[Union[str, datetime]] = None,
datetime: Optional[str] = None,
limit: Optional[int] = 10,
**kwargs,
) -> stac.ItemCollection:
Expand All @@ -247,7 +246,7 @@ def item_collection(
self,
collection_id: str,
bbox: Optional[List[Union[float, int]]] = None,
datetime: Optional[Union[str, datetime]] = None,
datetime: Optional[str] = None,
limit: int = 10,
token: str = None,
**kwargs,
Expand Down Expand Up @@ -392,6 +391,7 @@ def test_client_datetime_input_params():

class FakeClient(BaseCoreClient):
def post_search(self, search_request: BaseSearchPostRequest, **kwargs):
assert isinstance(search_request.datetime, str)
return search_request.datetime

def get_search(
Expand All @@ -400,10 +400,11 @@ def get_search(
ids: Optional[List[str]] = None,
bbox: Optional[List[NumType]] = None,
intersects: Optional[str] = None,
datetime: Optional[Union[str, datetime]] = None,
datetime: Optional[str] = None,
limit: Optional[int] = 10,
**kwargs,
):
assert isinstance(datetime, str)
return datetime

def get_item(self, item_id: str, collection_id: str, **kwargs) -> stac.Item:
Expand All @@ -419,7 +420,7 @@ def item_collection(
self,
collection_id: str,
bbox: Optional[List[Union[float, int]]] = None,
datetime: Optional[Union[str, datetime]] = None,
datetime: Optional[str] = None,
limit: int = 10,
token: str = None,
**kwargs,
Expand All @@ -439,16 +440,34 @@ def item_collection(
"datetime": "2020-01-01T00:00:00.00001Z",
},
)
get_search_zero = client.get(
"/search",
params={
"collections": ["test"],
"datetime": "2020-01-01T00:00:00.0000Z",
},
)
post_search = client.post(
"/search",
json={
"collections": ["test"],
"datetime": "2020-01-01T00:00:00.00001Z",
},
)
post_search_zero = client.post(
"/search",
json={
"collections": ["test"],
"datetime": "2020-01-01T00:00:00.0000Z",
},
)

assert get_search.status_code == 200, get_search.text
assert get_search.json() == "2020-01-01T00:00:00.000010+00:00"
assert get_search.json() == "2020-01-01T00:00:00.00001Z"
assert get_search_zero.status_code == 200, get_search_zero.text
assert get_search_zero.json() == "2020-01-01T00:00:00.0000Z"

assert post_search.status_code == 200, post_search.text
assert post_search.json() == "2020-01-01T00:00:00.00001Z"
assert post_search_zero.status_code == 200, post_search_zero.text
assert post_search_zero.json() == "2020-01-01T00:00:00.0000Z"
17 changes: 16 additions & 1 deletion stac_fastapi/api/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,24 @@ def test_create_get_request_model():

assert model.collections == ["test1", "test2"]
assert model.filter_crs == "epsg:4326"
d = model.datetime
d = model.start_date
assert d.microsecond == 10
assert not model.end_date

model = request_model(
datetime="2020-01-01T00:00:00.00001Z/2020-01-02T00:00:00.00001Z",
)
assert model.start_date
assert model.end_date

# invalid datetime format
with pytest.raises(HTTPException):
request_model(datetime="yo")

# Wrong order
with pytest.raises(HTTPException):
request_model(datetime="2020-01-02T00:00:00.00001Z/2020-01-01T00:00:00.00001Z")

app = FastAPI()

@app.get("/test")
Expand Down Expand Up @@ -92,6 +104,9 @@ def test_create_post_request_model(filter_val, passes):
assert model.filter == filter_val
assert model.datetime == "2020-01-01T00:00:00.00001Z"

with pytest.raises(ValidationError):
request_model(datetime="yo")


@pytest.mark.parametrize(
"sortby,passes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@
from stac_pydantic.shared import BBox
from typing_extensions import Annotated

from stac_fastapi.types.rfc3339 import DateTimeType
from stac_fastapi.types.search import (
APIRequest,
DatetimeMixin,
DateTimeQueryType,
Limit,
_bbox_converter,
_datetime_converter,
_validate_datetime,
)


@attr.s
class BaseCollectionSearchGetRequest(APIRequest):
class BaseCollectionSearchGetRequest(APIRequest, DatetimeMixin):
"""Basics additional Collection-Search parameters for the GET request."""

bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter)
datetime: Optional[DateTimeType] = attr.ib(
default=None, converter=_datetime_converter
)
datetime: DateTimeQueryType = attr.ib(default=None, validator=_validate_datetime)
limit: Annotated[
Optional[Limit],
Query(
Expand All @@ -38,8 +37,26 @@ class BaseCollectionSearchGetRequest(APIRequest):
class BaseCollectionSearchPostRequest(BaseModel):
"""Collection-Search POST model."""

bbox: Optional[BBox] = None
datetime: Optional[str] = None
bbox: Optional[BBox] = Field(
default=None,
description="Only return items intersecting this bounding box. Mutually exclusive with **intersects**.", # noqa: E501
json_schema_extra={
"example": [-175.05, -85.05, 175.05, 85.05],
},
)
datetime: Optional[str] = Field(
default=None,
description="""Only return items that have a temporal property that intersects this value.\n
Either a date-time or an interval, open or closed. Date and time expressions adhere to RFC 3339. Open intervals are expressed using double-dots.""", # noqa: E501
json_schema_extra={
"examples": {
"datetime": {"value": "2018-02-12T23:20:50Z"},
"closed-interval": {"value": "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z"},
"open-interval-from": {"value": "2018-02-12T00:00:00Z/.."},
"open-interval-to": {"value": "../2018-03-18T12:31:12Z"},
},
},
)
limit: Optional[Limit] = Field(
10,
description="Limits the number of results that are included in each page of the response (capped to 10_000).", # noqa: E501
Expand Down
10 changes: 2 additions & 8 deletions stac_fastapi/extensions/tests/test_collection_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,7 @@ def test_collection_search_extension_default():
assert response.is_success, response.json()
response_dict = response.json()
assert [-175.05, -85.05, 175.05, 85.05] == response_dict["bbox"]
assert [
"2020-06-13T13:00:00+00:00",
"2020-06-13T14:00:00+00:00",
] == response_dict["datetime"]
assert "2020-06-13T13:00:00Z/2020-06-13T14:00:00Z" == response_dict["datetime"]
assert 100 == response_dict["limit"]


Expand Down Expand Up @@ -211,10 +208,7 @@ def test_collection_search_extension_models():
assert response.is_success, response.json()
response_dict = response.json()
assert [-175.05, -85.05, 175.05, 85.05] == response_dict["bbox"]
assert [
"2020-06-13T13:00:00+00:00",
"2020-06-13T14:00:00+00:00",
] == response_dict["datetime"]
assert "2020-06-13T13:00:00Z/2020-06-13T14:00:00Z" == response_dict["datetime"]
assert 100 == response_dict["limit"]
assert ["EO", "Earth Observation"] == response_dict["q"]
assert "id='item_id' AND collection='collection_id'" == response_dict["filter"]
Expand Down
9 changes: 4 additions & 5 deletions stac_fastapi/types/stac_fastapi/types/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
from stac_fastapi.types.extension import ApiExtension
from stac_fastapi.types.requests import get_base_url
from stac_fastapi.types.rfc3339 import DateTimeType
from stac_fastapi.types.search import BaseSearchPostRequest

__all__ = [
Expand Down Expand Up @@ -497,7 +496,7 @@ def get_search(
ids: Optional[List[str]] = None,
bbox: Optional[BBox] = None,
intersects: Optional[Geometry] = None,
datetime: Optional[DateTimeType] = None,
datetime: Optional[str] = None,
limit: Optional[int] = 10,
**kwargs,
) -> stac.ItemCollection:
Expand Down Expand Up @@ -555,7 +554,7 @@ def item_collection(
self,
collection_id: str,
bbox: Optional[BBox] = None,
datetime: Optional[DateTimeType] = None,
datetime: Optional[str] = None,
limit: int = 10,
token: str = None,
**kwargs,
Expand Down Expand Up @@ -733,7 +732,7 @@ async def get_search(
ids: Optional[List[str]] = None,
bbox: Optional[BBox] = None,
intersects: Optional[Geometry] = None,
datetime: Optional[DateTimeType] = None,
datetime: Optional[str] = None,
limit: Optional[int] = 10,
**kwargs,
) -> stac.ItemCollection:
Expand Down Expand Up @@ -791,7 +790,7 @@ async def item_collection(
self,
collection_id: str,
bbox: Optional[BBox] = None,
datetime: Optional[DateTimeType] = None,
datetime: Optional[str] = None,
limit: int = 10,
token: str = None,
**kwargs,
Expand Down
Loading

0 comments on commit e3c138c

Please sign in to comment.