Skip to content

Commit fe37609

Browse files
authored
Update stac-fastapi parent libraries to 5.1.1 (#354)
**Related Issue(s):** - # **Description:** **PR Checklist:** - [x] Code is formatted and linted (run `pre-commit run --all-files`) - [x] Tests pass (run `make test`) - [x] Documentation has been updated to reflect changes, if applicable - [x] Changes are added to the changelog
1 parent 6b25e56 commit fe37609

File tree

13 files changed

+71
-59
lines changed

13 files changed

+71
-59
lines changed

.github/workflows/cicd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565

6666
strategy:
6767
matrix:
68-
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
68+
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13"]
6969
backend: [ "elasticsearch7", "elasticsearch8", "opensearch"]
7070

7171
name: Python ${{ matrix.python-version }} testing with ${{ matrix.backend }}

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
## [v4.0.0a0]
11+
1012
### Added
1113
- Added support for dynamically-generated queryables based on Elasticsearch/OpenSearch mappings, with extensible metadata augmentation [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351)
1214
- Included default queryables configuration for seamless integration. [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351)
1315

1416
### Changed
1517
- Refactored database logic to reduce duplication [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351)
1618
- Replaced `fastapi-slim` with `fastapi` dependency [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351)
19+
- Changed minimum Python version to 3.9 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354)
20+
- Updated stac-fastapi api, types, and extensions libraries to 5.1.1 from 3.0.0 and made various associated changes [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354)
21+
- Changed makefile commands from 'docker-compose' to 'docker compose' [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354)
1722

1823
### Fixed
1924
- Improved performance of `mk_actions` and `filter-links` methods [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351)
@@ -314,7 +319,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
314319
- Use genexp in execute_search and get_all_collections to return results.
315320
- Added db_to_stac serializer to item_collection method in core.py.
316321

317-
[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...main
322+
[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0a0...main
323+
[v4.0.0a0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...v4.0.0a0
318324
[v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v3.2.5
319325
[v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.3...v3.2.4
320326
[v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.2...v3.2.3

Makefile

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ OS_APP_PORT ?= 8082
1010
OS_HOST ?= docker.for.mac.localhost
1111
OS_PORT ?= 9202
1212

13-
run_es = docker-compose \
13+
run_es = docker compose \
1414
run \
1515
-p ${EXTERNAL_APP_PORT}:${ES_APP_PORT} \
1616
-e PY_IGNORE_IMPORTMISMATCH=1 \
1717
-e APP_HOST=${APP_HOST} \
1818
-e APP_PORT=${ES_APP_PORT} \
1919
app-elasticsearch
2020

21-
run_os = docker-compose \
21+
run_os = docker compose \
2222
run \
2323
-p ${EXTERNAL_APP_PORT}:${OS_APP_PORT} \
2424
-e PY_IGNORE_IMPORTMISMATCH=1 \
@@ -45,7 +45,7 @@ run-deploy-locally:
4545

4646
.PHONY: image-dev
4747
image-dev:
48-
docker-compose build
48+
docker compose build
4949

5050
.PHONY: docker-run-es
5151
docker-run-es: image-dev
@@ -66,28 +66,28 @@ docker-shell-os:
6666
.PHONY: test-elasticsearch
6767
test-elasticsearch:
6868
-$(run_es) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh elasticsearch:9200 && cd stac_fastapi/tests/ && pytest'
69-
docker-compose down
69+
docker compose down
7070

7171
.PHONY: test-opensearch
7272
test-opensearch:
7373
-$(run_os) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh opensearch:9202 && cd stac_fastapi/tests/ && pytest'
74-
docker-compose down
74+
docker compose down
7575

7676
.PHONY: test
7777
test:
7878
-$(run_es) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh elasticsearch:9200 && cd stac_fastapi/tests/ && pytest'
79-
docker-compose down
79+
docker compose down
8080

8181
-$(run_os) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh opensearch:9202 && cd stac_fastapi/tests/ && pytest'
82-
docker-compose down
82+
docker compose down
8383

8484
.PHONY: run-database-es
8585
run-database-es:
86-
docker-compose run --rm elasticsearch
86+
docker compose run --rm elasticsearch
8787

8888
.PHONY: run-database-os
8989
run-database-os:
90-
docker-compose run --rm opensearch
90+
docker compose run --rm opensearch
9191

9292
.PHONY: pybase-install
9393
pybase-install:
@@ -107,10 +107,10 @@ install-os: pybase-install
107107

108108
.PHONY: docs-image
109109
docs-image:
110-
docker-compose -f docker-compose.docs.yml \
110+
docker compose -f docker compose.docs.yml \
111111
build
112112

113113
.PHONY: docs
114114
docs: docs-image
115-
docker-compose -f docker-compose.docs.yml \
115+
docker compose -f docker compose.docs.yml \
116116
run docs

docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.9'
2-
31
services:
42
app-elasticsearch:
53
container_name: stac-fastapi-es

stac_fastapi/core/setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
"fastapi",
1010
"attrs>=23.2.0",
1111
"pydantic",
12-
"stac_pydantic>=3",
13-
"stac-fastapi.types==3.0.0",
14-
"stac-fastapi.api==3.0.0",
15-
"stac-fastapi.extensions==3.0.0",
12+
"stac_pydantic==3.1.*",
13+
"stac-fastapi.api==5.1.1",
14+
"stac-fastapi.extensions==5.1.1",
15+
"stac-fastapi.types==5.1.1",
1616
"orjson",
1717
"overrides",
1818
"geojson-pydantic",

stac_fastapi/core/stac_fastapi/core/core.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from stac_fastapi.types.core import AsyncBaseCoreClient, AsyncBaseTransactionsClient
3838
from stac_fastapi.types.extension import ApiExtension
3939
from stac_fastapi.types.requests import get_base_url
40-
from stac_fastapi.types.rfc3339 import DateTimeType
40+
from stac_fastapi.types.rfc3339 import DateTimeType, rfc3339_str_to_datetime
4141
from stac_fastapi.types.search import BaseSearchPostRequest
4242

4343
logger = logging.getLogger(__name__)
@@ -277,7 +277,7 @@ async def item_collection(
277277
self,
278278
collection_id: str,
279279
bbox: Optional[BBox] = None,
280-
datetime: Optional[DateTimeType] = None,
280+
datetime: Optional[str] = None,
281281
limit: Optional[int] = 10,
282282
token: Optional[str] = None,
283283
**kwargs,
@@ -287,7 +287,7 @@ async def item_collection(
287287
Args:
288288
collection_id (str): The identifier of the collection to read items from.
289289
bbox (Optional[BBox]): The bounding box to filter items by.
290-
datetime (Optional[DateTimeType]): The datetime range to filter items by.
290+
datetime (Optional[str]): The datetime range to filter items by.
291291
limit (int): The maximum number of items to return. The default value is 10.
292292
token (str): A token used for pagination.
293293
request (Request): The incoming request.
@@ -426,39 +426,50 @@ def _return_date(
426426

427427
return result
428428

429-
def _format_datetime_range(self, date_tuple: DateTimeType) -> str:
429+
def _format_datetime_range(self, date_str: str) -> str:
430430
"""
431-
Convert a tuple of datetime objects or None into a formatted string for API requests.
431+
Convert a datetime range string into a normalized UTC string for API requests using rfc3339_str_to_datetime.
432432
433433
Args:
434-
date_tuple (tuple): A tuple containing two elements, each can be a datetime object or None.
434+
date_str (str): A string containing two datetime values separated by a '/'.
435435
436436
Returns:
437-
str: A string formatted as 'YYYY-MM-DDTHH:MM:SS.sssZ/YYYY-MM-DDTHH:MM:SS.sssZ', with '..' used if any element is None.
437+
str: A string formatted as 'YYYY-MM-DDTHH:MM:SSZ/YYYY-MM-DDTHH:MM:SSZ', with '..' used if any element is None.
438438
"""
439439

440-
def format_datetime(dt):
441-
"""Format a single datetime object to the ISO8601 extended format with 'Z'."""
442-
return dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" if dt else ".."
443-
444-
start, end = date_tuple
445-
return f"{format_datetime(start)}/{format_datetime(end)}"
440+
def normalize(dt):
441+
dt = dt.strip()
442+
if not dt or dt == "..":
443+
return ".."
444+
dt_obj = rfc3339_str_to_datetime(dt)
445+
dt_utc = dt_obj.astimezone(timezone.utc)
446+
return dt_utc.strftime("%Y-%m-%dT%H:%M:%SZ")
447+
448+
if not isinstance(date_str, str):
449+
return "../.."
450+
if "/" not in date_str:
451+
return f"{normalize(date_str)}/{normalize(date_str)}"
452+
try:
453+
start, end = date_str.split("/", 1)
454+
except Exception:
455+
return "../.."
456+
return f"{normalize(start)}/{normalize(end)}"
446457

447458
async def get_search(
448459
self,
449460
request: Request,
450461
collections: Optional[List[str]] = None,
451462
ids: Optional[List[str]] = None,
452463
bbox: Optional[BBox] = None,
453-
datetime: Optional[DateTimeType] = None,
464+
datetime: Optional[str] = None,
454465
limit: Optional[int] = 10,
455466
query: Optional[str] = None,
456467
token: Optional[str] = None,
457468
fields: Optional[List[str]] = None,
458469
sortby: Optional[str] = None,
459470
q: Optional[List[str]] = None,
460471
intersects: Optional[str] = None,
461-
filter: Optional[str] = None,
472+
filter_expr: Optional[str] = None,
462473
filter_lang: Optional[str] = None,
463474
**kwargs,
464475
) -> stac_types.ItemCollection:
@@ -468,7 +479,7 @@ async def get_search(
468479
collections (Optional[List[str]]): List of collection IDs to search in.
469480
ids (Optional[List[str]]): List of item IDs to search for.
470481
bbox (Optional[BBox]): Bounding box to search in.
471-
datetime (Optional[DateTimeType]): Filter items based on the datetime field.
482+
datetime (Optional[str]): Filter items based on the datetime field.
472483
limit (Optional[int]): Maximum number of results to return.
473484
query (Optional[str]): Query string to filter the results.
474485
token (Optional[str]): Access token to use when searching the catalog.
@@ -495,7 +506,7 @@ async def get_search(
495506
}
496507

497508
if datetime:
498-
base_args["datetime"] = self._format_datetime_range(datetime)
509+
base_args["datetime"] = self._format_datetime_range(date_str=datetime)
499510

500511
if intersects:
501512
base_args["intersects"] = orjson.loads(unquote_plus(intersects))
@@ -506,12 +517,12 @@ async def get_search(
506517
for sort in sortby
507518
]
508519

509-
if filter:
510-
base_args["filter-lang"] = "cql2-json"
520+
if filter_expr:
521+
base_args["filter_lang"] = "cql2-json"
511522
base_args["filter"] = orjson.loads(
512-
unquote_plus(filter)
523+
unquote_plus(filter_expr)
513524
if filter_lang == "cql2-json"
514-
else to_cql2(parse_cql2_text(filter))
525+
else to_cql2(parse_cql2_text(filter_expr))
515526
)
516527

517528
if fields:
@@ -593,8 +604,8 @@ async def post_search(
593604
)
594605

595606
# only cql2_json is supported here
596-
if hasattr(search_request, "filter"):
597-
cql2_filter = getattr(search_request, "filter", None)
607+
if hasattr(search_request, "filter_expr"):
608+
cql2_filter = getattr(search_request, "filter_expr", None)
598609
try:
599610
search = self.database.apply_cql2_filter(search, cql2_filter)
600611
except Exception as e:

stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ async def aggregate(
338338
datetime: Optional[DateTimeType] = None,
339339
intersects: Optional[str] = None,
340340
filter_lang: Optional[str] = None,
341-
filter: Optional[str] = None,
341+
filter_expr: Optional[str] = None,
342342
aggregations: Optional[str] = None,
343343
ids: Optional[List[str]] = None,
344344
bbox: Optional[BBox] = None,
@@ -380,18 +380,18 @@ async def aggregate(
380380
if datetime:
381381
base_args["datetime"] = self._format_datetime_range(datetime)
382382

383-
if filter:
384-
base_args["filter"] = self.get_filter(filter, filter_lang)
383+
if filter_expr:
384+
base_args["filter"] = self.get_filter(filter_expr, filter_lang)
385385
aggregate_request = EsAggregationExtensionPostRequest(**base_args)
386386
else:
387387
# Workaround for optional path param in POST requests
388388
if "collections" in path:
389389
collection_id = path.split("/")[2]
390390

391391
filter_lang = "cql2-json"
392-
if aggregate_request.filter:
393-
aggregate_request.filter = self.get_filter(
394-
aggregate_request.filter, filter_lang
392+
if aggregate_request.filter_expr:
393+
aggregate_request.filter_expr = self.get_filter(
394+
aggregate_request.filter_expr, filter_lang
395395
)
396396

397397
if collection_id:
@@ -465,10 +465,10 @@ async def aggregate(
465465
detail=f"Aggregation {agg_name} not supported at catalog level",
466466
)
467467

468-
if aggregate_request.filter:
468+
if aggregate_request.filter_expr:
469469
try:
470470
search = self.database.apply_cql2_filter(
471-
search, aggregate_request.filter
471+
search, aggregate_request.filter_expr
472472
)
473473
except Exception as e:
474474
raise HTTPException(
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""library version."""
2-
__version__ = "3.2.5"
2+
__version__ = "4.0.0a0"

stac_fastapi/elasticsearch/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
desc = f.read()
77

88
install_requires = [
9-
"stac-fastapi.core==3.2.5",
9+
"stac-fastapi.core==4.0.0a0",
1010
"elasticsearch[async]==8.11.0",
1111
"elasticsearch-dsl==8.11.0",
1212
"uvicorn",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""library version."""
2-
__version__ = "3.2.5"
2+
__version__ = "4.0.0a0"

stac_fastapi/opensearch/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
desc = f.read()
77

88
install_requires = [
9-
"stac-fastapi.core==3.2.5",
9+
"stac-fastapi.core==4.0.0a0",
1010
"opensearch-py==2.4.2",
1111
"opensearch-py[async]==2.4.2",
1212
"uvicorn",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""library version."""
2-
__version__ = "3.2.5"
2+
__version__ = "4.0.0a0"

stac_fastapi/tests/resources/test_item.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import uuid
44
from copy import deepcopy
5-
from datetime import datetime, timedelta, timezone
5+
from datetime import datetime, timedelta
66
from random import randint
77
from urllib.parse import parse_qs, urlparse, urlsplit
88

@@ -478,13 +478,10 @@ async def test_item_search_temporal_window_timezone_get(
478478
app_client, ctx, load_test_data
479479
):
480480
"""Test GET search with spatio-temporal query ending with Zulu and pagination(core)"""
481-
tzinfo = timezone(timedelta(hours=1))
482481
test_item = load_test_data("test_item.json")
483482
item_date = rfc3339_str_to_datetime(test_item["properties"]["datetime"])
484483
item_date_before = item_date - timedelta(seconds=1)
485-
item_date_before = item_date_before.replace(tzinfo=tzinfo)
486484
item_date_after = item_date + timedelta(seconds=1)
487-
item_date_after = item_date_after.replace(tzinfo=tzinfo)
488485

489486
params = {
490487
"collections": test_item["collection"],

0 commit comments

Comments
 (0)