From 268c1616ae1794b1ce0d0d21c02689d31003164e Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:26:47 +0800 Subject: [PATCH 01/22] change from 'docker-compose' --- Makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 9a3f23ce..e965d785 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ OS_APP_PORT ?= 8082 OS_HOST ?= docker.for.mac.localhost OS_PORT ?= 9202 -run_es = docker-compose \ +run_es = docker compose \ run \ -p ${EXTERNAL_APP_PORT}:${ES_APP_PORT} \ -e PY_IGNORE_IMPORTMISMATCH=1 \ @@ -18,7 +18,7 @@ run_es = docker-compose \ -e APP_PORT=${ES_APP_PORT} \ app-elasticsearch -run_os = docker-compose \ +run_os = docker compose \ run \ -p ${EXTERNAL_APP_PORT}:${OS_APP_PORT} \ -e PY_IGNORE_IMPORTMISMATCH=1 \ @@ -45,7 +45,7 @@ run-deploy-locally: .PHONY: image-dev image-dev: - docker-compose build + docker compose build .PHONY: docker-run-es docker-run-es: image-dev @@ -66,28 +66,28 @@ docker-shell-os: .PHONY: test-elasticsearch test-elasticsearch: -$(run_es) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh elasticsearch:9200 && cd stac_fastapi/tests/ && pytest' - docker-compose down + docker compose down .PHONY: test-opensearch test-opensearch: -$(run_os) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh opensearch:9202 && cd stac_fastapi/tests/ && pytest' - docker-compose down + docker compose down .PHONY: test test: -$(run_es) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh elasticsearch:9200 && cd stac_fastapi/tests/ && pytest' - docker-compose down + docker compose down -$(run_os) /bin/bash -c 'export && ./scripts/wait-for-it-es.sh opensearch:9202 && cd stac_fastapi/tests/ && pytest' - docker-compose down + docker compose down .PHONY: run-database-es run-database-es: - docker-compose run --rm elasticsearch + docker compose run --rm elasticsearch .PHONY: run-database-os run-database-os: - docker-compose run --rm opensearch + docker compose run --rm opensearch .PHONY: pybase-install pybase-install: @@ -107,10 +107,10 @@ install-os: pybase-install .PHONY: docs-image docs-image: - docker-compose -f docker-compose.docs.yml \ + docker compose -f docker compose.docs.yml \ build .PHONY: docs docs: docs-image - docker-compose -f docker-compose.docs.yml \ + docker compose -f docker compose.docs.yml \ run docs \ No newline at end of file From 4cbb4e62d232331a5f45e268e8216898461b6695 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:27:02 +0800 Subject: [PATCH 02/22] remove 'version' --- docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index da4633b9..8ec0701b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.9' - services: app-elasticsearch: container_name: stac-fastapi-es From a93608d695c2716a1bf17addeb0e2cbf0ba82240 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:27:30 +0800 Subject: [PATCH 03/22] update stac-fastapi tp 5.1 --- stac_fastapi/core/setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stac_fastapi/core/setup.py b/stac_fastapi/core/setup.py index aedbe231..bd15097e 100644 --- a/stac_fastapi/core/setup.py +++ b/stac_fastapi/core/setup.py @@ -9,10 +9,10 @@ "fastapi", "attrs>=23.2.0", "pydantic", - "stac_pydantic>=3", - "stac-fastapi.types==3.0.0", - "stac-fastapi.api==3.0.0", - "stac-fastapi.extensions==3.0.0", + "stac_pydantic==3.1.*", + "stac-fastapi.api>=5.1,<6.0", + "stac-fastapi.extensions>=5.1,<6.0", + "stac-fastapi.types>=5.1,<6.0", "orjson", "overrides", "geojson-pydantic", From e06f8c0f3e29ef6cc1e302874cfd4e6618dcb322 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:28:06 +0800 Subject: [PATCH 04/22] change format datetime function --- stac_fastapi/core/stac_fastapi/core/core.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index 11bd34b4..f971d332 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -426,23 +426,20 @@ def _return_date( return result - def _format_datetime_range(self, date_tuple: DateTimeType) -> str: + def _format_datetime_range(self, date_tuple: str) -> str: """ - Convert a tuple of datetime objects or None into a formatted string for API requests. + Convert a datetime range into a formatted string. Args: - date_tuple (tuple): A tuple containing two elements, each can be a datetime object or None. + date_tuple (str): A string containing two datetime values separated by a '/'. Returns: str: A string formatted as 'YYYY-MM-DDTHH:MM:SS.sssZ/YYYY-MM-DDTHH:MM:SS.sssZ', with '..' used if any element is None. """ - - def format_datetime(dt): - """Format a single datetime object to the ISO8601 extended format with 'Z'.""" - return dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" if dt else ".." - - start, end = date_tuple - return f"{format_datetime(start)}/{format_datetime(end)}" + start, end = date_tuple.split("/") + start = start.replace("+01:00", "Z") if start else ".." + end = end.replace("+01:00", "Z") if end else ".." + return f"{start}/{end}" async def get_search( self, From 893ed7f5ecf2b57ab07476668b4f6fa128c0c3fb Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:28:21 +0800 Subject: [PATCH 05/22] alias not working? --- .../core/extensions/aggregation.py | 10 +++---- .../tests/extensions/test_aggregation.py | 28 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py b/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py index 2cf880c9..a3698caf 100644 --- a/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py +++ b/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py @@ -389,9 +389,9 @@ async def aggregate( collection_id = path.split("/")[2] filter_lang = "cql2-json" - if aggregate_request.filter: - aggregate_request.filter = self.get_filter( - aggregate_request.filter, filter_lang + if aggregate_request.filter_expr: + aggregate_request.filter_expr = self.get_filter( + aggregate_request.filter_expr, filter_lang ) if collection_id: @@ -465,10 +465,10 @@ async def aggregate( detail=f"Aggregation {agg_name} not supported at catalog level", ) - if aggregate_request.filter: + if aggregate_request.filter_expr: try: search = self.database.apply_cql2_filter( - search, aggregate_request.filter + search, aggregate_request.filter_expr ) except Exception as e: raise HTTPException( diff --git a/stac_fastapi/tests/extensions/test_aggregation.py b/stac_fastapi/tests/extensions/test_aggregation.py index 94a5c247..80720e75 100644 --- a/stac_fastapi/tests/extensions/test_aggregation.py +++ b/stac_fastapi/tests/extensions/test_aggregation.py @@ -178,8 +178,8 @@ async def test_aggregate_datetime_in_range(app_client, ctx): @pytest.mark.asyncio async def test_aggregate_filter_extension_eq_post(app_client, ctx): params = { - "filter": {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, - "filter-lang": "cql2-json", + "filter_expr": {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, + "filter_lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -190,8 +190,8 @@ async def test_aggregate_filter_extension_eq_post(app_client, ctx): @pytest.mark.asyncio async def test_aggregate_filter_extension_neq_post(app_client, ctx): params = { - "filter": {"op": "<>", "args": [{"property": "id"}, ctx.item["id"]]}, - "filter-lang": "cql2-json", + "filter_expr": {"op": "<>", "args": [{"property": "id"}, ctx.item["id"]]}, + "filter_lang": "cql2-json", "aggregations": ["total_count"], "collections": [ctx.item["collection"]], } @@ -222,14 +222,14 @@ async def test_aggregate_extension_gte_get(app_client, ctx): async def test_aggregate_filter_extension_gte_post(app_client, ctx): # there's one item that can match, so one of these queries should match it and the other shouldn't params = { - "filter": { + "filter_expr": { "op": "<=", "args": [ {"property": "properties.proj:epsg"}, ctx.item["properties"]["proj:epsg"], ], }, - "filter-lang": "cql2-json", + "filter_lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -238,14 +238,14 @@ async def test_aggregate_filter_extension_gte_post(app_client, ctx): assert resp.json()["aggregations"][0]["value"] == 1 params = { - "filter": { + "filter_expr": { "op": ">", "args": [ {"property": "properties.proj:epsg"}, ctx.item["properties"]["proj:epsg"], ], }, - "filter-lang": "cql2-json", + "filter_lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -271,7 +271,7 @@ async def test_search_aggregate_extension_wildcard_cql2(app_client, ctx): multi_char = ctx.item["id"][:-3] + "%" params = { - "filter": { + "filter_expr": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, @@ -291,7 +291,7 @@ async def test_search_aggregate_extension_wildcard_cql2(app_client, ctx): }, ], }, - "filter-lang": "cql2-json", + "filter_lang": "cql2-json", "aggregations": ["total_count"], } @@ -307,7 +307,7 @@ async def test_aggregate_filter_extension_wildcard_es(app_client, ctx): multi_char = ctx.item["id"][:-3] + "*" params = { - "filter": { + "filter_expr": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, @@ -327,7 +327,7 @@ async def test_aggregate_filter_extension_wildcard_es(app_client, ctx): }, ], }, - "filter-lang": "cql2-json", + "filter_lang": "cql2-json", "aggregations": ["total_count"], } @@ -370,7 +370,7 @@ async def test_aggregate_filter_extension_in_no_list(app_client, ctx): product_id = ctx.item["properties"]["landsat:product_id"] params = { - "filter": { + "filter_expr": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, @@ -383,7 +383,7 @@ async def test_aggregate_filter_extension_in_no_list(app_client, ctx): }, ], }, - "filter-lang": "cql2-json", + "filter_lang": "cql2-json", "aggregations": ["total_count"], } From 605533bd0e97d336e0fd1bcc59c317fd37cf56ef Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Sat, 12 Apr 2025 20:38:12 +0800 Subject: [PATCH 06/22] update core to v4.0.0 --- stac_fastapi/core/setup.py | 6 +++--- stac_fastapi/core/stac_fastapi/core/version.py | 2 +- stac_fastapi/elasticsearch/setup.py | 2 +- .../elasticsearch/stac_fastapi/elasticsearch/version.py | 2 +- stac_fastapi/opensearch/setup.py | 2 +- stac_fastapi/opensearch/stac_fastapi/opensearch/version.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stac_fastapi/core/setup.py b/stac_fastapi/core/setup.py index bd15097e..e871498a 100644 --- a/stac_fastapi/core/setup.py +++ b/stac_fastapi/core/setup.py @@ -10,9 +10,9 @@ "attrs>=23.2.0", "pydantic", "stac_pydantic==3.1.*", - "stac-fastapi.api>=5.1,<6.0", - "stac-fastapi.extensions>=5.1,<6.0", - "stac-fastapi.types>=5.1,<6.0", + "stac-fastapi.api>=5.1.0,<6.0", + "stac-fastapi.extensions>=5.1.0,<6.0", + "stac-fastapi.types>=5.1.0,<6.0", "orjson", "overrides", "geojson-pydantic", diff --git a/stac_fastapi/core/stac_fastapi/core/version.py b/stac_fastapi/core/stac_fastapi/core/version.py index ca97d75a..6356730f 100644 --- a/stac_fastapi/core/stac_fastapi/core/version.py +++ b/stac_fastapi/core/stac_fastapi/core/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "3.2.5" +__version__ = "4.0.0" diff --git a/stac_fastapi/elasticsearch/setup.py b/stac_fastapi/elasticsearch/setup.py index 7fb82dc7..a1feef31 100644 --- a/stac_fastapi/elasticsearch/setup.py +++ b/stac_fastapi/elasticsearch/setup.py @@ -6,7 +6,7 @@ desc = f.read() install_requires = [ - "stac-fastapi.core==3.2.5", + "stac-fastapi.core==4.0.0", "elasticsearch[async]==8.11.0", "elasticsearch-dsl==8.11.0", "uvicorn", diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py index ca97d75a..6356730f 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "3.2.5" +__version__ = "4.0.0" diff --git a/stac_fastapi/opensearch/setup.py b/stac_fastapi/opensearch/setup.py index 0befa10e..89c4d088 100644 --- a/stac_fastapi/opensearch/setup.py +++ b/stac_fastapi/opensearch/setup.py @@ -6,7 +6,7 @@ desc = f.read() install_requires = [ - "stac-fastapi.core==3.2.5", + "stac-fastapi.core==4.0.0", "opensearch-py==2.4.2", "opensearch-py[async]==2.4.2", "uvicorn", diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py index ca97d75a..6356730f 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "3.2.5" +__version__ = "4.0.0" From c0204695f5a9f430a97acd8762eeb1219798d780 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 15:03:26 +0800 Subject: [PATCH 07/22] pin stac-fastapi to 5.1.1 --- stac_fastapi/core/setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stac_fastapi/core/setup.py b/stac_fastapi/core/setup.py index e871498a..43b3e911 100644 --- a/stac_fastapi/core/setup.py +++ b/stac_fastapi/core/setup.py @@ -10,9 +10,9 @@ "attrs>=23.2.0", "pydantic", "stac_pydantic==3.1.*", - "stac-fastapi.api>=5.1.0,<6.0", - "stac-fastapi.extensions>=5.1.0,<6.0", - "stac-fastapi.types>=5.1.0,<6.0", + "stac-fastapi.api==5.1.1", + "stac-fastapi.extensions==5.1.1", + "stac-fastapi.types==5.1.1", "orjson", "overrides", "geojson-pydantic", From cf4fca038cf5eec0f7bb736cf22ec70213cd3ca0 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 15:08:12 +0800 Subject: [PATCH 08/22] try no-cache-dir --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index a966248b..7e557b72 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -94,7 +94,7 @@ jobs: - name: Install core library stac-fastapi run: | - pip install ./stac_fastapi/core + pip install --no-cache-dir --upgrade ./stac_fastapi/core - name: Install elasticsearch stac-fastapi run: | From b93e0ef0f5c2bbe690f8b8b9ce25c38af24eae8e Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 15:13:31 +0800 Subject: [PATCH 09/22] comment out cache --- .github/workflows/cicd.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7e557b72..987dfe59 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -78,9 +78,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - cache: 'pip' - cache-dependency-path: | - **/setup.py + # cache: 'pip' + # cache-dependency-path: | + # **/setup.py - name: Lint code if: ${{ matrix.python-version == 3.11 }} @@ -98,11 +98,11 @@ jobs: - name: Install elasticsearch stac-fastapi run: | - pip install ./stac_fastapi/elasticsearch[dev,server] + pip install --no-cache-dir ./stac_fastapi/elasticsearch[dev,server] - name: Install opensearch stac-fastapi run: | - pip install ./stac_fastapi/opensearch[dev,server] + pip install --no-cache-dir ./stac_fastapi/opensearch[dev,server] - name: Install pytest-timeout run: | From a21b8c86de8809c0243f895b62f89ac132a28f12 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 15:16:38 +0800 Subject: [PATCH 10/22] remove python 3.8 --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 987dfe59..ef4d16e6 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -65,7 +65,7 @@ jobs: strategy: matrix: - python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13"] backend: [ "elasticsearch7", "elasticsearch8", "opensearch"] name: Python ${{ matrix.python-version }} testing with ${{ matrix.backend }} From a1f077052b334fe46d7379636f03fc2ab6a199ec Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 17:11:33 +0800 Subject: [PATCH 11/22] fix tests --- stac_fastapi/tests/extensions/test_aggregation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stac_fastapi/tests/extensions/test_aggregation.py b/stac_fastapi/tests/extensions/test_aggregation.py index 80720e75..a456c793 100644 --- a/stac_fastapi/tests/extensions/test_aggregation.py +++ b/stac_fastapi/tests/extensions/test_aggregation.py @@ -178,7 +178,7 @@ async def test_aggregate_datetime_in_range(app_client, ctx): @pytest.mark.asyncio async def test_aggregate_filter_extension_eq_post(app_client, ctx): params = { - "filter_expr": {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, + "filter": {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, "filter_lang": "cql2-json", "aggregations": ["total_count"], } @@ -190,7 +190,7 @@ async def test_aggregate_filter_extension_eq_post(app_client, ctx): @pytest.mark.asyncio async def test_aggregate_filter_extension_neq_post(app_client, ctx): params = { - "filter_expr": {"op": "<>", "args": [{"property": "id"}, ctx.item["id"]]}, + "filter": {"op": "<>", "args": [{"property": "id"}, ctx.item["id"]]}, "filter_lang": "cql2-json", "aggregations": ["total_count"], "collections": [ctx.item["collection"]], @@ -222,7 +222,7 @@ async def test_aggregate_extension_gte_get(app_client, ctx): async def test_aggregate_filter_extension_gte_post(app_client, ctx): # there's one item that can match, so one of these queries should match it and the other shouldn't params = { - "filter_expr": { + "filter": { "op": "<=", "args": [ {"property": "properties.proj:epsg"}, @@ -238,7 +238,7 @@ async def test_aggregate_filter_extension_gte_post(app_client, ctx): assert resp.json()["aggregations"][0]["value"] == 1 params = { - "filter_expr": { + "filter": { "op": ">", "args": [ {"property": "properties.proj:epsg"}, @@ -271,7 +271,7 @@ async def test_search_aggregate_extension_wildcard_cql2(app_client, ctx): multi_char = ctx.item["id"][:-3] + "%" params = { - "filter_expr": { + "filter": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, @@ -307,7 +307,7 @@ async def test_aggregate_filter_extension_wildcard_es(app_client, ctx): multi_char = ctx.item["id"][:-3] + "*" params = { - "filter_expr": { + "filter": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, @@ -370,7 +370,7 @@ async def test_aggregate_filter_extension_in_no_list(app_client, ctx): product_id = ctx.item["properties"]["landsat:product_id"] params = { - "filter_expr": { + "filter": { "op": "and", "args": [ {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, From d9a85029b17be77e9997d39bc525c938ae1dc082 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 17:17:07 +0800 Subject: [PATCH 12/22] fix filter extension --- stac_fastapi/core/stac_fastapi/core/core.py | 14 +++++++------- .../stac_fastapi/core/extensions/aggregation.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index f971d332..a48333e7 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -455,7 +455,7 @@ async def get_search( sortby: Optional[str] = None, q: Optional[List[str]] = None, intersects: Optional[str] = None, - filter: Optional[str] = None, + filter_expr: Optional[str] = None, filter_lang: Optional[str] = None, **kwargs, ) -> stac_types.ItemCollection: @@ -503,12 +503,12 @@ async def get_search( for sort in sortby ] - if filter: - base_args["filter-lang"] = "cql2-json" + if filter_expr: + base_args["filter_lang"] = "cql2-json" base_args["filter"] = orjson.loads( - unquote_plus(filter) + unquote_plus(filter_expr) if filter_lang == "cql2-json" - else to_cql2(parse_cql2_text(filter)) + else to_cql2(parse_cql2_text(filter_expr)) ) if fields: @@ -590,8 +590,8 @@ async def post_search( ) # only cql2_json is supported here - if hasattr(search_request, "filter"): - cql2_filter = getattr(search_request, "filter", None) + if hasattr(search_request, "filter_expr"): + cql2_filter = getattr(search_request, "filter_expr", None) try: search = self.database.apply_cql2_filter(search, cql2_filter) except Exception as e: diff --git a/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py b/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py index a3698caf..43bd543c 100644 --- a/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py +++ b/stac_fastapi/core/stac_fastapi/core/extensions/aggregation.py @@ -338,7 +338,7 @@ async def aggregate( datetime: Optional[DateTimeType] = None, intersects: Optional[str] = None, filter_lang: Optional[str] = None, - filter: Optional[str] = None, + filter_expr: Optional[str] = None, aggregations: Optional[str] = None, ids: Optional[List[str]] = None, bbox: Optional[BBox] = None, @@ -380,8 +380,8 @@ async def aggregate( if datetime: base_args["datetime"] = self._format_datetime_range(datetime) - if filter: - base_args["filter"] = self.get_filter(filter, filter_lang) + if filter_expr: + base_args["filter"] = self.get_filter(filter_expr, filter_lang) aggregate_request = EsAggregationExtensionPostRequest(**base_args) else: # Workaround for optional path param in POST requests From 2c2bd424ea2f95a4361d44ae0828cf196c2e91aa Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 18:36:20 +0800 Subject: [PATCH 13/22] update changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b4d793..7911514b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [v4.0.0] + ### Added - 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) - Included default queryables configuration for seamless integration. [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) @@ -14,6 +16,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Refactored database logic to reduce duplication [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) - Replaced `fastapi-slim` with `fastapi` dependency [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) +- Changed minimum Python version to 3.9 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) +- Updated stac-fastapi api, types, and extensions libraries to 5.1.1 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) ### Fixed - Improved performance of `mk_actions` and `filter-links` methods [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) @@ -314,7 +318,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Use genexp in execute_search and get_all_collections to return results. - Added db_to_stac serializer to item_collection method in core.py. -[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...main +[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0...main +[v4.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v4.0.0 [v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v3.2.5 [v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.3...v3.2.4 [v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.2...v3.2.3 From ba11623ee989d15355f7a50ccffb67c96ad39478 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 18:36:59 +0800 Subject: [PATCH 14/22] add cache back --- .github/workflows/cicd.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index ef4d16e6..864b52e3 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -78,9 +78,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - # cache: 'pip' - # cache-dependency-path: | - # **/setup.py + cache: 'pip' + cache-dependency-path: | + **/setup.py - name: Lint code if: ${{ matrix.python-version == 3.11 }} @@ -94,15 +94,15 @@ jobs: - name: Install core library stac-fastapi run: | - pip install --no-cache-dir --upgrade ./stac_fastapi/core + pip install ./stac_fastapi/core - name: Install elasticsearch stac-fastapi run: | - pip install --no-cache-dir ./stac_fastapi/elasticsearch[dev,server] + pip install ./stac_fastapi/elasticsearch[dev,server] - name: Install opensearch stac-fastapi run: | - pip install --no-cache-dir ./stac_fastapi/opensearch[dev,server] + pip install ./stac_fastapi/opensearch[dev,server] - name: Install pytest-timeout run: | From 55a36d31cf826d8a3df1e4edf4d596a0481846e5 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 18:49:55 +0800 Subject: [PATCH 15/22] switch back to filter-lang --- stac_fastapi/tests/extensions/test_aggregation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stac_fastapi/tests/extensions/test_aggregation.py b/stac_fastapi/tests/extensions/test_aggregation.py index a456c793..94a5c247 100644 --- a/stac_fastapi/tests/extensions/test_aggregation.py +++ b/stac_fastapi/tests/extensions/test_aggregation.py @@ -179,7 +179,7 @@ async def test_aggregate_datetime_in_range(app_client, ctx): async def test_aggregate_filter_extension_eq_post(app_client, ctx): params = { "filter": {"op": "=", "args": [{"property": "id"}, ctx.item["id"]]}, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -191,7 +191,7 @@ async def test_aggregate_filter_extension_eq_post(app_client, ctx): async def test_aggregate_filter_extension_neq_post(app_client, ctx): params = { "filter": {"op": "<>", "args": [{"property": "id"}, ctx.item["id"]]}, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], "collections": [ctx.item["collection"]], } @@ -229,7 +229,7 @@ async def test_aggregate_filter_extension_gte_post(app_client, ctx): ctx.item["properties"]["proj:epsg"], ], }, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -245,7 +245,7 @@ async def test_aggregate_filter_extension_gte_post(app_client, ctx): ctx.item["properties"]["proj:epsg"], ], }, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } resp = await app_client.post("/aggregate", json=params) @@ -291,7 +291,7 @@ async def test_search_aggregate_extension_wildcard_cql2(app_client, ctx): }, ], }, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } @@ -327,7 +327,7 @@ async def test_aggregate_filter_extension_wildcard_es(app_client, ctx): }, ], }, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } @@ -383,7 +383,7 @@ async def test_aggregate_filter_extension_in_no_list(app_client, ctx): }, ], }, - "filter_lang": "cql2-json", + "filter-lang": "cql2-json", "aggregations": ["total_count"], } From 64a238d7d1ea4509913449d15ff582998aa2b52d Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 19:11:23 +0800 Subject: [PATCH 16/22] change type to str --- stac_fastapi/core/stac_fastapi/core/core.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index a48333e7..6df8458b 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -277,7 +277,7 @@ async def item_collection( self, collection_id: str, bbox: Optional[BBox] = None, - datetime: Optional[DateTimeType] = None, + datetime: Optional[str] = None, limit: Optional[int] = 10, token: Optional[str] = None, **kwargs, @@ -287,7 +287,7 @@ async def item_collection( Args: collection_id (str): The identifier of the collection to read items from. bbox (Optional[BBox]): The bounding box to filter items by. - datetime (Optional[DateTimeType]): The datetime range to filter items by. + datetime (Optional[str]): The datetime range to filter items by. limit (int): The maximum number of items to return. The default value is 10. token (str): A token used for pagination. request (Request): The incoming request. @@ -426,7 +426,7 @@ def _return_date( return result - def _format_datetime_range(self, date_tuple: str) -> str: + def _format_datetime_range(self, date_str: str) -> str: """ Convert a datetime range into a formatted string. @@ -436,7 +436,7 @@ def _format_datetime_range(self, date_tuple: str) -> str: Returns: str: A string formatted as 'YYYY-MM-DDTHH:MM:SS.sssZ/YYYY-MM-DDTHH:MM:SS.sssZ', with '..' used if any element is None. """ - start, end = date_tuple.split("/") + start, end = date_str.split("/") start = start.replace("+01:00", "Z") if start else ".." end = end.replace("+01:00", "Z") if end else ".." return f"{start}/{end}" @@ -447,7 +447,7 @@ async def get_search( collections: Optional[List[str]] = None, ids: Optional[List[str]] = None, bbox: Optional[BBox] = None, - datetime: Optional[DateTimeType] = None, + datetime: Optional[str] = None, limit: Optional[int] = 10, query: Optional[str] = None, token: Optional[str] = None, @@ -465,7 +465,7 @@ async def get_search( collections (Optional[List[str]]): List of collection IDs to search in. ids (Optional[List[str]]): List of item IDs to search for. bbox (Optional[BBox]): Bounding box to search in. - datetime (Optional[DateTimeType]): Filter items based on the datetime field. + datetime (Optional[str]): Filter items based on the datetime field. limit (Optional[int]): Maximum number of results to return. query (Optional[str]): Query string to filter the results. token (Optional[str]): Access token to use when searching the catalog. @@ -492,7 +492,7 @@ async def get_search( } if datetime: - base_args["datetime"] = self._format_datetime_range(datetime) + base_args["datetime"] = self._format_datetime_range(date_str=datetime) if intersects: base_args["intersects"] = orjson.loads(unquote_plus(intersects)) From 4688ab350dfb0868cd916c67890057b184305ec5 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Mon, 14 Apr 2025 19:12:14 +0800 Subject: [PATCH 17/22] add context --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7911514b..62e715f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Refactored database logic to reduce duplication [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) - Replaced `fastapi-slim` with `fastapi` dependency [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) - Changed minimum Python version to 3.9 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) -- Updated stac-fastapi api, types, and extensions libraries to 5.1.1 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) +- Updated stac-fastapi api, types, and extensions libraries to 5.1.1 and made various associated changes [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) ### Fixed - Improved performance of `mk_actions` and `filter-links` methods [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) From 1de9ba56ae25c195d66adebddab31204bd230031 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Tue, 15 Apr 2025 00:21:29 +0800 Subject: [PATCH 18/22] changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62e715f2..7e854c70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -319,7 +319,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added db_to_stac serializer to item_collection method in core.py. [Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0...main -[v4.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v4.0.0 +[v4.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...v4.0.0 [v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v3.2.5 [v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.3...v3.2.4 [v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.2...v3.2.3 From 3325672f11f4d1937fe7abc2caa675e795fc429f Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Tue, 15 Apr 2025 10:29:51 +0800 Subject: [PATCH 19/22] add more info to changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e854c70..6fbddfe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Refactored database logic to reduce duplication [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) - Replaced `fastapi-slim` with `fastapi` dependency [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) - Changed minimum Python version to 3.9 [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) -- Updated stac-fastapi api, types, and extensions libraries to 5.1.1 and made various associated changes [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) +- 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) +- Changed makefile commands from 'docker-compose' to 'docker compose' [#354](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/354) ### Fixed - Improved performance of `mk_actions` and `filter-links` methods [#351](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/351) From 6ada768a4b77dfeffd6abff652372da71d77bc6a Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Tue, 15 Apr 2025 18:42:27 +0800 Subject: [PATCH 20/22] change version to 4.0.0a0 --- CHANGELOG.md | 6 +++--- stac_fastapi/core/stac_fastapi/core/version.py | 2 +- stac_fastapi/elasticsearch/setup.py | 2 +- .../elasticsearch/stac_fastapi/elasticsearch/version.py | 2 +- stac_fastapi/opensearch/setup.py | 2 +- stac_fastapi/opensearch/stac_fastapi/opensearch/version.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fbddfe8..f29766ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## [v4.0.0] +## [v4.0.0a0] ### Added - 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) @@ -319,8 +319,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Use genexp in execute_search and get_all_collections to return results. - Added db_to_stac serializer to item_collection method in core.py. -[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0...main -[v4.0.0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...v4.0.0 +[Unreleased]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v4.0.0a0...main +[v4.0.0a0]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.5...v4.0.0a0 [v3.2.5]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.4...v3.2.5 [v3.2.4]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.3...v3.2.4 [v3.2.3]: https://github.com/stac-utils/stac-fastapi-elasticsearch/tree/v3.2.2...v3.2.3 diff --git a/stac_fastapi/core/stac_fastapi/core/version.py b/stac_fastapi/core/stac_fastapi/core/version.py index 6356730f..3488c82b 100644 --- a/stac_fastapi/core/stac_fastapi/core/version.py +++ b/stac_fastapi/core/stac_fastapi/core/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "4.0.0" +__version__ = "4.0.0a0" diff --git a/stac_fastapi/elasticsearch/setup.py b/stac_fastapi/elasticsearch/setup.py index a1feef31..3355dbe3 100644 --- a/stac_fastapi/elasticsearch/setup.py +++ b/stac_fastapi/elasticsearch/setup.py @@ -6,7 +6,7 @@ desc = f.read() install_requires = [ - "stac-fastapi.core==4.0.0", + "stac-fastapi.core==4.0.0a0", "elasticsearch[async]==8.11.0", "elasticsearch-dsl==8.11.0", "uvicorn", diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py index 6356730f..3488c82b 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "4.0.0" +__version__ = "4.0.0a0" diff --git a/stac_fastapi/opensearch/setup.py b/stac_fastapi/opensearch/setup.py index 89c4d088..8cae5dce 100644 --- a/stac_fastapi/opensearch/setup.py +++ b/stac_fastapi/opensearch/setup.py @@ -6,7 +6,7 @@ desc = f.read() install_requires = [ - "stac-fastapi.core==4.0.0", + "stac-fastapi.core==4.0.0a0", "opensearch-py==2.4.2", "opensearch-py[async]==2.4.2", "uvicorn", diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py index 6356730f..3488c82b 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/version.py @@ -1,2 +1,2 @@ """library version.""" -__version__ = "4.0.0" +__version__ = "4.0.0a0" From 7ded416f8eb92ab9be38850aecdd1f41029623c8 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Wed, 16 Apr 2025 13:05:30 +0800 Subject: [PATCH 21/22] improve datetime range --- stac_fastapi/core/stac_fastapi/core/core.py | 32 ++++++++++++++++----- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index 6df8458b..9df989b5 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -428,18 +428,36 @@ def _return_date( def _format_datetime_range(self, date_str: str) -> str: """ - Convert a datetime range into a formatted string. + Convert a datetime range string into a normalized UTC string for API requests. Args: - date_tuple (str): A string containing two datetime values separated by a '/'. + date_str (str): A string containing two datetime values separated by a '/'. Returns: - str: A string formatted as 'YYYY-MM-DDTHH:MM:SS.sssZ/YYYY-MM-DDTHH:MM:SS.sssZ', with '..' used if any element is None. + str: A string formatted as 'YYYY-MM-DDTHH:MM:SSZ/YYYY-MM-DDTHH:MM:SSZ', with '..' used if any element is None. """ - start, end = date_str.split("/") - start = start.replace("+01:00", "Z") if start else ".." - end = end.replace("+01:00", "Z") if end else ".." - return f"{start}/{end}" + if not isinstance(date_str, str) or "/" not in date_str: + return "../.." + try: + start, end = date_str.split("/", 1) + except Exception: + return "../.." + + def normalize(dt): + dt = dt.strip() + if not dt or dt == "..": + return ".." + # Replace any timezone offset with 'Z' + if dt.endswith("Z"): + return dt + if "+" in dt: + return dt[: dt.index("+")] + "Z" + if "-" in dt[10:]: # skip date part, look for tz in time part + idx = dt.index("-", 10) + return dt[:idx] + "Z" + return dt # fallback, return as-is + + return f"{normalize(start)}/{normalize(end)}" async def get_search( self, From 8b32ba8f24e07c11d63b594fc9f10e152afc3a92 Mon Sep 17 00:00:00 2001 From: jonhealy1 Date: Wed, 16 Apr 2025 20:06:43 +0800 Subject: [PATCH 22/22] update date format logic, test --- stac_fastapi/core/stac_fastapi/core/core.py | 30 +++++++++------------ stac_fastapi/tests/resources/test_item.py | 5 +--- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index 9df989b5..1e96c371 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -37,7 +37,7 @@ from stac_fastapi.types.core import AsyncBaseCoreClient, AsyncBaseTransactionsClient 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.rfc3339 import DateTimeType, rfc3339_str_to_datetime from stac_fastapi.types.search import BaseSearchPostRequest logger = logging.getLogger(__name__) @@ -428,7 +428,7 @@ def _return_date( def _format_datetime_range(self, date_str: str) -> str: """ - Convert a datetime range string into a normalized UTC string for API requests. + Convert a datetime range string into a normalized UTC string for API requests using rfc3339_str_to_datetime. Args: date_str (str): A string containing two datetime values separated by a '/'. @@ -436,27 +436,23 @@ def _format_datetime_range(self, date_str: str) -> str: Returns: str: A string formatted as 'YYYY-MM-DDTHH:MM:SSZ/YYYY-MM-DDTHH:MM:SSZ', with '..' used if any element is None. """ - if not isinstance(date_str, str) or "/" not in date_str: - return "../.." - try: - start, end = date_str.split("/", 1) - except Exception: - return "../.." def normalize(dt): dt = dt.strip() if not dt or dt == "..": return ".." - # Replace any timezone offset with 'Z' - if dt.endswith("Z"): - return dt - if "+" in dt: - return dt[: dt.index("+")] + "Z" - if "-" in dt[10:]: # skip date part, look for tz in time part - idx = dt.index("-", 10) - return dt[:idx] + "Z" - return dt # fallback, return as-is + dt_obj = rfc3339_str_to_datetime(dt) + dt_utc = dt_obj.astimezone(timezone.utc) + return dt_utc.strftime("%Y-%m-%dT%H:%M:%SZ") + if not isinstance(date_str, str): + return "../.." + if "/" not in date_str: + return f"{normalize(date_str)}/{normalize(date_str)}" + try: + start, end = date_str.split("/", 1) + except Exception: + return "../.." return f"{normalize(start)}/{normalize(end)}" async def get_search( diff --git a/stac_fastapi/tests/resources/test_item.py b/stac_fastapi/tests/resources/test_item.py index 904adbbf..5313b1fa 100644 --- a/stac_fastapi/tests/resources/test_item.py +++ b/stac_fastapi/tests/resources/test_item.py @@ -2,7 +2,7 @@ import os import uuid from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta from random import randint from urllib.parse import parse_qs, urlparse, urlsplit @@ -478,13 +478,10 @@ async def test_item_search_temporal_window_timezone_get( app_client, ctx, load_test_data ): """Test GET search with spatio-temporal query ending with Zulu and pagination(core)""" - tzinfo = timezone(timedelta(hours=1)) test_item = load_test_data("test_item.json") item_date = rfc3339_str_to_datetime(test_item["properties"]["datetime"]) item_date_before = item_date - timedelta(seconds=1) - item_date_before = item_date_before.replace(tzinfo=tzinfo) item_date_after = item_date + timedelta(seconds=1) - item_date_after = item_date_after.replace(tzinfo=tzinfo) params = { "collections": test_item["collection"],