Skip to content

Commit 5ded0c6

Browse files
Release/v4.0.0 (#786)
* prepare for 4.0 * Bump version: 3.0.5 → 4.0.0 * Update docs/src/migrations/v4.0.0.md Co-authored-by: Pete Gadomski <[email protected]> --------- Co-authored-by: Pete Gadomski <[email protected]>
1 parent 2a72400 commit 5ded0c6

File tree

13 files changed

+208
-47
lines changed

13 files changed

+208
-47
lines changed

.github/workflows/cicd.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
13+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
1414
timeout-minutes: 20
1515

1616
steps:

CHANGES.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## [4.0.0] - 2025-01-17
44

55
### Changed
66

77
* use `string` type instead of python `datetime.datetime` for datetime parameter in `BaseSearchGetRequest`, `ItemCollectionUri` and `BaseCollectionSearchGetRequest` GET models
88
* rename `filter` to `filter_expr` for `FilterExtensionGetRequest` and `FilterExtensionPostRequest` attributes to avoid conflict with python filter method
9+
* remove deprecated `post_request_model` attribute in `BaseCoreClient` and `AsyncBaseCoreClient`
10+
* remove `python3.8` support
911

1012
### Fixed
1113

@@ -524,7 +526,8 @@ Full changelog: https://stac-utils.github.io/stac-fastapi/migrations/v3.0.0/#cha
524526

525527
* First PyPi release!
526528

527-
[Unreleased]: <https://github.com/stac-utils/stac-fastapi/compare/3.0.5..main>
529+
[Unreleased]: <https://github.com/stac-utils/stac-fastapi/compare/4.0.0..main>
530+
[4.0.0]: <https://github.com/stac-utils/stac-fastapi/compare/3.0.5..4.0.0>
528531
[3.0.5]: <https://github.com/stac-utils/stac-fastapi/compare/3.0.4..3.0.5>
529532
[3.0.4]: <https://github.com/stac-utils/stac-fastapi/compare/3.0.3..3.0.4>
530533
[3.0.3]: <https://github.com/stac-utils/stac-fastapi/compare/3.0.2..3.0.3>

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.0.5
1+
4.0.0

docs/mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ nav:
8080
- version: api/stac_fastapi/types/version.md
8181
- Migration Guides:
8282
- v2.5 -> v3.0: migrations/v3.0.0.md
83+
- v3.0 -> v4.0: migrations/v4.0.0.md
8384
- Performance Benchmarks: benchmarks.html
8485
- Development - Contributing: "contributing.md"
8586
- Release Notes: "release-notes.md"

docs/src/migrations/v4.0.0.md

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# stac-fastapi v4.0 Migration Guide
2+
3+
This document aims to help you update your application from **stac-fastapi** 3.0 to 4.0
4+
5+
## CHANGELOG
6+
### Changed
7+
8+
* use `string` type instead of python `datetime.datetime` for datetime parameter in `BaseSearchGetRequest`, `ItemCollectionUri` and `BaseCollectionSearchGetRequest` GET models
9+
* rename `filter` to `filter_expr` for `FilterExtensionGetRequest` and `FilterExtensionPostRequest` attributes to avoid conflict with python filter method
10+
* remove `post_request_model` attribute in `BaseCoreClient` and `AsyncBaseCoreClient`
11+
* remove `python3.8` support
12+
13+
### Fixed
14+
15+
* Support multiple proxy servers in the `forwarded` header in `ProxyHeaderMiddleware` ([#782](https://github.com/stac-utils/stac-fastapi/pull/782))
16+
17+
## Datetime type in GET request models
18+
19+
While the POST request models are created using stac-pydantic, the GET request models are python `attrs` classes (~dataclasses).
20+
In 4.0, we've decided to change how the `datetime` attribute was defined in `BaseSearchGetRequest`, `ItemCollectionUri` and `BaseCollectionSearchGetRequest` models to match
21+
the `datetime` definition/validation done by the pydantic model. This mostly mean that the datetime attribute forwarded to the GET endpoints will now be of type string (forwarded from the user input).
22+
23+
```python
24+
from starlette.testclient import TestClient
25+
from stac_fastapi.api.app import StacApi
26+
from stac_fastapi.types.config import ApiSettings
27+
from stac_fastapi.types.core import BaseCoreClient
28+
29+
class DummyCoreClient(BaseCoreClient):
30+
def all_collections(self, *args, **kwargs):
31+
raise NotImplementedError
32+
33+
def get_collection(self, *args, **kwargs):
34+
raise NotImplementedError
35+
36+
def get_item(self, *args, **kwargs):
37+
raise NotImplementedError
38+
39+
def get_search(self, *args, datetime = None, **kwargs):
40+
# Return True if datetime is a string
41+
return isinstance(datetime, str)
42+
43+
def post_search(self, *args, **kwargs):
44+
raise NotImplementedError
45+
46+
def item_collection(self, *args, **kwargs):
47+
raise NotImplementedError
48+
49+
api = StacApi(
50+
settings=ApiSettings(enable_response_models=False),
51+
client=DummyCoreClient(),
52+
extensions=[],
53+
)
54+
55+
56+
# before
57+
with TestClient(api.app) as client:
58+
response = client.get(
59+
"/search",
60+
params={
61+
"datetime": "2020-01-01T00:00:00.00001Z",
62+
},
63+
)
64+
assert response.json() == False
65+
66+
# now
67+
with TestClient(api.app) as client:
68+
response = client.get(
69+
"/search",
70+
params={
71+
"datetime": "2020-01-01T00:00:00.00001Z",
72+
},
73+
)
74+
assert response.json() == True
75+
```
76+
77+
#### Start/End dates
78+
79+
Following stac-pydantic's `Search` model, we've added class attributes to easily retrieve the `parsed` dates:
80+
81+
```python
82+
from stac_fastapi.types.search import BaseSearchGetRequest
83+
84+
# Interval
85+
search = BaseSearchGetRequest(datetime="2020-01-01T00:00:00.00001Z/2020-01-02T00:00:00.00001Z")
86+
87+
search.parse_datetime()
88+
>>> (datetime.datetime(2020, 1, 1, 0, 0, 0, 10, tzinfo=datetime.timezone.utc), datetime.datetime(2020, 1, 2, 0, 0, 0, 10, tzinfo=datetime.timezone.utc))
89+
90+
search.start_date
91+
>>> datetime.datetime(2020, 1, 1, 0, 0, 0, 10, tzinfo=datetime.timezone.utc)
92+
93+
search.end_date
94+
>>> datetime.datetime(2020, 1, 2, 0, 0, 0, 10, tzinfo=datetime.timezone.utc)
95+
96+
# Single date
97+
search = BaseSearchGetRequest(datetime="2020-01-01T00:00:00.00001Z")
98+
99+
search.parse_datetime()
100+
>>> datetime.datetime(2020, 1, 1, 0, 0, 0, 10, tzinfo=datetime.timezone.utc)
101+
102+
search.start_date
103+
>>> datetime.datetime(2020, 1, 1, 0, 0, 0, 10, tzinfo=datetime.timezone.utc)
104+
105+
search.end_date
106+
>>> None
107+
```
108+
109+
## Filter extension
110+
111+
We've renamed the `filter` attribute to `filter_expr` in the `FilterExtensionGetRequest` and `FilterExtensionPostRequest` models to avoid any conflict with python `filter` method. This change means GET endpoints with the filter extension enabled will receive `filter_expr=` option instead of `filter=`. Same for POST endpoints where the `body` will now have a `.filter_expr` instead of a `filter` attribute.
112+
113+
Note: This change does not affect the `input` because we use `aliases`.
114+
115+
```python
116+
from starlette.testclient import TestClient
117+
from stac_fastapi.api.app import StacApi
118+
from stac_fastapi.api.models import create_get_request_model, create_post_request_model
119+
from stac_fastapi.extensions.core import FilterExtension
120+
from stac_fastapi.types.config import ApiSettings
121+
from stac_fastapi.types.core import BaseCoreClient
122+
123+
class DummyCoreClient(BaseCoreClient):
124+
def all_collections(self, *args, **kwargs):
125+
raise NotImplementedError
126+
127+
def get_collection(self, *args, **kwargs):
128+
raise NotImplementedError
129+
130+
def get_item(self, *args, **kwargs):
131+
raise NotImplementedError
132+
133+
def get_search(self, *args, **kwargs):
134+
return kwargs
135+
136+
def post_search(self, *args, **kwargs):
137+
return args[0].model_dump()
138+
139+
def item_collection(self, *args, **kwargs):
140+
raise NotImplementedError
141+
142+
extensions = [FilterExtension()]
143+
api = StacApi(
144+
settings=ApiSettings(enable_response_models=False),
145+
client=DummyCoreClient(),
146+
extensions=extensions,
147+
search_get_request_model=create_get_request_model(extensions),
148+
search_post_request_model=create_post_request_model(extensions),
149+
)
150+
151+
152+
# before
153+
with TestClient(api.app) as client:
154+
response = client.post(
155+
"/search",
156+
json={
157+
"filter": {"op": "=", "args": [{"property": "test_property"}, "test-value"]},
158+
},
159+
)
160+
assert response.json()["filter"]
161+
162+
response = client.get(
163+
"/search",
164+
params={
165+
"filter": "id='item_id' AND collection='collection_id'",
166+
},
167+
)
168+
assert response.json()["filter"]
169+
170+
# now
171+
with TestClient(api.app) as client:
172+
response = client.post(
173+
"/search",
174+
json={
175+
"filter": {"op": "=", "args": [{"property": "test_property"}, "test-value"]},
176+
},
177+
)
178+
assert response.json()["filter_expr"]
179+
180+
response = client.get(
181+
"/search",
182+
params={
183+
"filter": "id='item_id' AND collection='collection_id'",
184+
},
185+
)
186+
assert response.json()["filter_expr"]
187+
```
188+
189+

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tool.ruff]
2-
target-version = "py38" # minimum supported version
2+
target-version = "py39" # minimum supported version
33
line-length = 90
44

55
[tool.ruff.lint]
@@ -24,7 +24,7 @@ section-order = ["future", "standard-library", "third-party", "first-party", "lo
2424
quote-style = "double"
2525

2626
[tool.bumpversion]
27-
current_version = "3.0.5"
27+
current_version = "4.0.0"
2828
parse = """(?x)
2929
(?P<major>\\d+)\\.
3030
(?P<minor>\\d+)\\.

stac_fastapi/api/setup.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
install_requires = [
99
"brotli_asgi",
10-
"stac-fastapi.types~=3.0",
10+
"stac-fastapi.types~=4.0",
1111
]
1212

1313
extra_reqs = {
@@ -31,12 +31,11 @@
3131
description="An implementation of STAC API based on the FastAPI framework.",
3232
long_description=desc,
3333
long_description_content_type="text/markdown",
34-
python_requires=">=3.8",
34+
python_requires=">=3.9",
3535
classifiers=[
3636
"Intended Audience :: Developers",
3737
"Intended Audience :: Information Technology",
3838
"Intended Audience :: Science/Research",
39-
"Programming Language :: Python :: 3.8",
4039
"Programming Language :: Python :: 3.9",
4140
"Programming Language :: Python :: 3.10",
4241
"Programming Language :: Python :: 3.11",
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Library version."""
22

3-
__version__ = "3.0.5"
3+
__version__ = "4.0.0"

stac_fastapi/extensions/setup.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
desc = f.read()
88

99
install_requires = [
10-
"stac-fastapi.types~=3.0",
11-
"stac-fastapi.api~=3.0",
10+
"stac-fastapi.types~=4.0",
11+
"stac-fastapi.api~=4.0",
1212
]
1313

1414
extra_reqs = {
@@ -28,12 +28,11 @@
2828
description="An implementation of STAC API based on the FastAPI framework.",
2929
long_description=desc,
3030
long_description_content_type="text/markdown",
31-
python_requires=">=3.8",
31+
python_requires=">=3.9",
3232
classifiers=[
3333
"Intended Audience :: Developers",
3434
"Intended Audience :: Information Technology",
3535
"Intended Audience :: Science/Research",
36-
"Programming Language :: Python :: 3.8",
3736
"Programming Language :: Python :: 3.9",
3837
"Programming Language :: Python :: 3.10",
3938
"Programming Language :: Python :: 3.11",
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Library version."""
22

3-
__version__ = "3.0.5"
3+
__version__ = "4.0.0"

stac_fastapi/types/setup.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,11 @@
3030
description="An implementation of STAC API based on the FastAPI framework.",
3131
long_description=desc,
3232
long_description_content_type="text/markdown",
33-
python_requires=">=3.8",
33+
python_requires=">=3.9",
3434
classifiers=[
3535
"Intended Audience :: Developers",
3636
"Intended Audience :: Information Technology",
3737
"Intended Audience :: Science/Research",
38-
"Programming Language :: Python :: 3.8",
3938
"Programming Language :: Python :: 3.9",
4039
"Programming Language :: Python :: 3.10",
4140
"Programming Language :: Python :: 3.11",

stac_fastapi/types/stac_fastapi/types/core.py

-29
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Base clients."""
22

33
import abc
4-
import warnings
54
from typing import Any, Dict, List, Optional, Union
65
from urllib.parse import urljoin
76

@@ -341,20 +340,6 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
341340
factory=lambda: BASE_CONFORMANCE_CLASSES
342341
)
343342
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))
344-
post_request_model = attr.ib(default=None)
345-
346-
@post_request_model.validator
347-
def _deprecate_post_model(self, attribute, value):
348-
"""Check and raise warning if `post_request_model` is set."""
349-
if value is not None:
350-
warnings.warn(
351-
"`post_request_model` attribute is deprecated and will be removed in 3.1",
352-
DeprecationWarning,
353-
)
354-
355-
def __attrs_post_init__(self):
356-
"""Set default value for post_request_model."""
357-
self.post_request_model = self.post_request_model or BaseSearchPostRequest
358343

359344
def conformance_classes(self) -> List[str]:
360345
"""Generate conformance classes by adding extension conformance to base
@@ -586,20 +571,6 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC):
586571
factory=lambda: BASE_CONFORMANCE_CLASSES
587572
)
588573
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))
589-
post_request_model = attr.ib(default=None)
590-
591-
@post_request_model.validator
592-
def _deprecate_post_model(self, attribute, value):
593-
"""Check and raise warning if `post_request_model` is set."""
594-
if value is not None:
595-
warnings.warn(
596-
"`post_request_model` attribute is deprecated and will be removed in 3.1",
597-
DeprecationWarning,
598-
)
599-
600-
def __attrs_post_init__(self):
601-
"""Set default value for post_request_model."""
602-
self.post_request_model = self.post_request_model or BaseSearchPostRequest
603574

604575
def conformance_classes(self) -> List[str]:
605576
"""Generate conformance classes by adding extension conformance to base
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Library version."""
22

3-
__version__ = "3.0.5"
3+
__version__ = "4.0.0"

0 commit comments

Comments
 (0)