Skip to content

Commit

Permalink
Update for stac-fastapi v3.0.0a release (#234)
Browse files Browse the repository at this point in the history
**Related Issue(s):**

- #238 
- #247 
- #249 
- stac-utils/stac-fastapi-pgstac#108
- stac-utils/stac-fastapi#685
- stac-utils/stac-fastapi#687
- stac-utils/stac-fastapi#690
- 

**Description:**

Update stac-fastapi parent libraries to v3.0.0a. There are quite a few
changes made in this pr.

**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

---------

Co-authored-by: pedro-cf <[email protected]>
  • Loading branch information
jonhealy1 and pedro-cf authored May 10, 2024
1 parent 55dd87e commit a1d076a
Show file tree
Hide file tree
Showing 22 changed files with 551 additions and 460 deletions.
20 changes: 12 additions & 8 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
- 9202:9202
strategy:
matrix:
python-version: [ "3.8", "3.9", "3.10", "3.11"]
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12"]

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

Expand All @@ -78,13 +78,21 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Lint code
uses: pre-commit/[email protected]
if: ${{ matrix.python-version == 3.11 }}
run: |
python -m pip install pre-commit
pre-commit run --all-files
- name: Install pipenv
run: |
python -m pip install --upgrade pipenv wheel
- name: Install core library stac-fastapi
run: |
pip install ./stac_fastapi/core
- name: Install elasticsearch stac-fastapi
run: |
pip install ./stac_fastapi/elasticsearch[dev,server]
Expand All @@ -93,16 +101,12 @@ jobs:
run: |
pip install ./stac_fastapi/opensearch[dev,server]
- name: Install core library stac-fastapi
run: |
pip install ./stac_fastapi/core
- name: Run test suite against Elasticsearch 7.x
run: |
pipenv run pytest -svvv
env:
ENVIRONMENT: testing
ES_PORT: 9200
ES_PORT: 9400
ES_HOST: 172.17.0.1
ES_USE_SSL: false
ES_VERIFY_CERTS: false
Expand All @@ -113,7 +117,7 @@ jobs:
pipenv run pytest -svvv
env:
ENVIRONMENT: testing
ES_PORT: 9400
ES_PORT: 9200
ES_HOST: 172.17.0.1
ES_USE_SSL: false
ES_VERIFY_CERTS: false
Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Support for Python 3.12 [#234](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/234)

### Changed

- Updated stac-fastapi parent libraries to v3.0.0a0 [#234](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/234)
- Removed pystac dependency [#234](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/234)

### Fixed

- Fixed issue where paginated search queries would return a `next_token` on the last page [#243](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/243)

## [v2.4.1]

### Added
Expand All @@ -15,7 +28,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Fixed

- Fixed issue where paginated search queries would return a `next_token` on the last page [#243](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/243)
- Fixed issue where searches return an empty `links` array [#241](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/241)

## [v2.4.0]
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/Dockerfile.dev.es
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM python:3.10-slim
# update apt pkgs, and install build-essential for ciso8601
RUN apt-get update && \
apt-get -y upgrade && \
apt-get install -y build-essential && \
apt-get install -y build-essential git && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

Expand Down
18 changes: 9 additions & 9 deletions stac_fastapi/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
desc = f.read()

install_requires = [
"fastapi",
"attrs",
"pydantic[dotenv]<2",
"stac_pydantic==2.0.*",
"stac-fastapi.types==2.5.5.post1",
"stac-fastapi.api==2.5.5.post1",
"stac-fastapi.extensions==2.5.5.post1",
"pystac[validation]",
"fastapi-slim",
"attrs>=23.2.0",
"pydantic[dotenv]",
"stac_pydantic>=3",
"stac-fastapi.types==3.0.0a",
"stac-fastapi.api==3.0.0a",
"stac-fastapi.extensions==3.0.0a",
"orjson",
"overrides",
"geojson-pydantic",
"pygeofilter==0.2.1",
"typing_extensions==4.4.0",
"typing_extensions==4.8.0",
]

setup(
Expand All @@ -35,6 +34,7 @@
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: MIT License",
],
url="https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch",
Expand Down
67 changes: 48 additions & 19 deletions stac_fastapi/core/stac_fastapi/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import re
from datetime import datetime as datetime_type
from datetime import timezone
from enum import Enum
from typing import Any, Dict, List, Optional, Set, Type, Union
from urllib.parse import unquote_plus, urljoin

Expand All @@ -14,6 +15,7 @@
from pydantic import ValidationError
from pygeofilter.backends.cql2_json import to_cql2
from pygeofilter.parsers.cql2_text import parse as parse_cql2_text
from stac_pydantic import Collection, Item, ItemCollection
from stac_pydantic.links import Relations
from stac_pydantic.shared import BBox, MimeTypes
from stac_pydantic.version import STAC_VERSION
Expand All @@ -25,7 +27,6 @@
from stac_fastapi.core.session import Session
from stac_fastapi.core.types.core import (
AsyncBaseCoreClient,
AsyncBaseFiltersClient,
AsyncBaseTransactionsClient,
)
from stac_fastapi.extensions.third_party.bulk_transactions import (
Expand All @@ -36,11 +37,11 @@
from stac_fastapi.types import stac as stac_types
from stac_fastapi.types.config import Settings
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
from stac_fastapi.types.core import AsyncBaseFiltersClient
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
from stac_fastapi.types.stac import Collection, Collections, Item, ItemCollection

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -189,7 +190,7 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage:

return landing_page

async def all_collections(self, **kwargs) -> Collections:
async def all_collections(self, **kwargs) -> stac_types.Collections:
"""Read all collections from the database.
Args:
Expand Down Expand Up @@ -221,9 +222,11 @@ async def all_collections(self, **kwargs) -> Collections:
next_link = PagingLinks(next=next_token, request=request).link_next()
links.append(next_link)

return Collections(collections=collections, links=links)
return stac_types.Collections(collections=collections, links=links)

async def get_collection(self, collection_id: str, **kwargs) -> Collection:
async def get_collection(
self, collection_id: str, **kwargs
) -> stac_types.Collection:
"""Get a collection from the database by its id.
Args:
Expand All @@ -250,7 +253,7 @@ async def item_collection(
limit: int = 10,
token: str = None,
**kwargs,
) -> ItemCollection:
) -> stac_types.ItemCollection:
"""Read items from a specific collection in the database.
Args:
Expand Down Expand Up @@ -320,14 +323,16 @@ async def item_collection(

links = await PagingLinks(request=request, next=next_token).get_links()

return ItemCollection(
return stac_types.ItemCollection(
type="FeatureCollection",
features=items,
links=links,
context=context_obj,
)

async def get_item(self, item_id: str, collection_id: str, **kwargs) -> Item:
async def get_item(
self, item_id: str, collection_id: str, **kwargs
) -> stac_types.Item:
"""Get an item from the database based on its id and collection id.
Args:
Expand Down Expand Up @@ -399,6 +404,24 @@ def _return_date(

return result

def _format_datetime_range(self, date_tuple: DateTimeType) -> str:
"""
Convert a tuple of datetime objects or None into a formatted string for API requests.
Args:
date_tuple (tuple): A tuple containing two elements, each can be a datetime object or None.
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)}"

async def get_search(
self,
request: Request,
Expand All @@ -415,7 +438,7 @@ async def get_search(
filter: Optional[str] = None,
filter_lang: Optional[str] = None,
**kwargs,
) -> ItemCollection:
) -> stac_types.ItemCollection:
"""Get search results from the database.
Args:
Expand Down Expand Up @@ -455,7 +478,7 @@ async def get_search(
filter_lang = match.group(1)

if datetime:
base_args["datetime"] = datetime
base_args["datetime"] = self._format_datetime_range(datetime)

if intersects:
base_args["intersects"] = orjson.loads(unquote_plus(intersects))
Expand Down Expand Up @@ -502,7 +525,7 @@ async def get_search(

async def post_search(
self, search_request: BaseSearchPostRequest, request: Request
) -> ItemCollection:
) -> stac_types.ItemCollection:
"""
Perform a POST search on the catalog.
Expand Down Expand Up @@ -552,8 +575,10 @@ async def post_search(
for field_name, expr in search_request.query.items():
field = "properties__" + field_name
for op, value in expr.items():
# Convert enum to string
operator = op.value if isinstance(op, Enum) else op
search = self.database.apply_stacql_filter(
search=search, op=op, field=field, value=value
search=search, op=operator, field=field, value=value
)

# only cql2_json is supported here
Expand Down Expand Up @@ -619,7 +644,7 @@ async def post_search(

links = await PagingLinks(request=request, next=next_token).get_links()

return ItemCollection(
return stac_types.ItemCollection(
type="FeatureCollection",
features=items,
links=links,
Expand All @@ -637,7 +662,7 @@ class TransactionsClient(AsyncBaseTransactionsClient):

@overrides
async def create_item(
self, collection_id: str, item: stac_types.Item, **kwargs
self, collection_id: str, item: Union[Item, ItemCollection], **kwargs
) -> Optional[stac_types.Item]:
"""Create an item in the collection.
Expand All @@ -654,6 +679,7 @@ async def create_item(
ConflictError: If the item in the specified collection already exists.
"""
item = item.model_dump(mode="json")
base_url = str(kwargs["request"].base_url)

# If a feature collection is posted
Expand All @@ -677,7 +703,7 @@ async def create_item(

@overrides
async def update_item(
self, collection_id: str, item_id: str, item: stac_types.Item, **kwargs
self, collection_id: str, item_id: str, item: Item, **kwargs
) -> stac_types.Item:
"""Update an item in the collection.
Expand All @@ -694,13 +720,14 @@ async def update_item(
NotFound: If the specified collection is not found in the database.
"""
item = item.model_dump(mode="json")
base_url = str(kwargs["request"].base_url)
now = datetime_type.now(timezone.utc).isoformat().replace("+00:00", "Z")
item["properties"]["updated"] = now

await self.database.check_collection_exists(collection_id)
await self.delete_item(item_id=item_id, collection_id=collection_id)
await self.create_item(collection_id=collection_id, item=item, **kwargs)
await self.create_item(collection_id=collection_id, item=Item(**item), **kwargs)

return ItemSerializer.db_to_stac(item, base_url)

Expand All @@ -722,7 +749,7 @@ async def delete_item(

@overrides
async def create_collection(
self, collection: stac_types.Collection, **kwargs
self, collection: Collection, **kwargs
) -> stac_types.Collection:
"""Create a new collection in the database.
Expand All @@ -736,17 +763,17 @@ async def create_collection(
Raises:
ConflictError: If the collection already exists.
"""
collection = collection.model_dump(mode="json")
base_url = str(kwargs["request"].base_url)
collection = self.database.collection_serializer.stac_to_db(
collection, base_url
)
await self.database.create_collection(collection=collection)

return CollectionSerializer.db_to_stac(collection, base_url)

@overrides
async def update_collection(
self, collection: stac_types.Collection, **kwargs
self, collection: Collection, **kwargs
) -> stac_types.Collection:
"""
Update a collection.
Expand All @@ -766,6 +793,8 @@ async def update_collection(
A STAC collection that has been updated in the database.
"""
collection = collection.model_dump(mode="json")

base_url = str(kwargs["request"].base_url)

collection_id = kwargs["request"].query_params.get(
Expand Down
Loading

0 comments on commit a1d076a

Please sign in to comment.