Skip to content

keep datetime as string type in GET Request models #780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading