Skip to content

Commit 80c246f

Browse files
authored
Merge pull request #127 from stac-utils/stac-fastapi-2.4.3
Stac fastapi 2.4.3
2 parents bf25781 + f127403 commit 80c246f

File tree

9 files changed

+159
-42
lines changed

9 files changed

+159
-42
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
810
### Added
911

12+
- Added bbox and datetime parameters and functionality to item_collection https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/127
13+
- Added collection_id parameter to create_item function https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/127
14+
- Added item_id and collection_id to update_item https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/127
1015
- The default Collection objects index can be overridden by the `STAC_COLLECTIONS_INDEX` environment variable.
1116
- The default Item objects index prefix can be overridden by the `STAC_ITEMS_INDEX_PREFIX` environment variable.
1217

18+
19+
### Changed
20+
21+
- Updated core stac-fastapi libraries to 2.4.3 from 2.3.0 https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/127
22+
1323
## [v0.2.0]
1424

1525
### Deprecated

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Elasticsearch backend for stac-fastapi.
44

5-
**WIP** This backend does not have any production deployments yet, so use the pgstac backend instead if that's what you need.
5+
Join our [Gitter](https://gitter.im/stac-fastapi-elasticsearch/community) page
66

77
For changes, see the [Changelog](CHANGELOG.md).
88

stac_fastapi/elasticsearch/setup.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
"attrs",
1111
"pydantic[dotenv]",
1212
"stac_pydantic==2.0.*",
13-
"stac-fastapi.types==2.3.0",
14-
"stac-fastapi.api==2.3.0",
15-
"stac-fastapi.extensions==2.3.0",
13+
"stac-fastapi.types==2.4.3",
14+
"stac-fastapi.api==2.4.3",
15+
"stac-fastapi.extensions==2.4.3",
1616
"elasticsearch[async]==7.17.8",
1717
"elasticsearch-dsl==7.4.0",
1818
"pystac[validation]",

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/core.py

+51-22
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from urllib.parse import urljoin
88

99
import attr
10-
import stac_pydantic.api
1110
from fastapi import HTTPException
1211
from overrides import overrides
1312
from pydantic import ValidationError
@@ -21,7 +20,6 @@
2120
from stac_fastapi.elasticsearch.models.links import PagingLinks
2221
from stac_fastapi.elasticsearch.serializers import CollectionSerializer, ItemSerializer
2322
from stac_fastapi.elasticsearch.session import Session
24-
from stac_fastapi.extensions.core.filter.request import FilterLang
2523
from stac_fastapi.extensions.third_party.bulk_transactions import (
2624
BaseBulkTransactionsClient,
2725
Items,
@@ -33,6 +31,7 @@
3331
AsyncBaseTransactionsClient,
3432
)
3533
from stac_fastapi.types.links import CollectionLinks
34+
from stac_fastapi.types.search import BaseSearchPostRequest
3635
from stac_fastapi.types.stac import Collection, Collections, Item, ItemCollection
3736

3837
logger = logging.getLogger(__name__)
@@ -91,21 +90,49 @@ async def get_collection(self, collection_id: str, **kwargs) -> Collection:
9190

9291
@overrides
9392
async def item_collection(
94-
self, collection_id: str, limit: int = 10, token: str = None, **kwargs
93+
self,
94+
collection_id: str,
95+
bbox: Optional[List[NumType]] = None,
96+
datetime: Union[str, datetime_type, None] = None,
97+
limit: int = 10,
98+
token: str = None,
99+
**kwargs,
95100
) -> ItemCollection:
96101
"""Read an item collection from the database."""
97102
request: Request = kwargs["request"]
98-
base_url = str(kwargs["request"].base_url)
103+
base_url = str(request.base_url)
104+
105+
collection = await self.get_collection(
106+
collection_id=collection_id, request=request
107+
)
108+
collection_id = collection.get("id")
109+
if collection_id is None:
110+
raise HTTPException(status_code=404, detail="Collection not found")
111+
112+
search = self.database.make_search()
113+
search = self.database.apply_collections_filter(
114+
search=search, collection_ids=[collection_id]
115+
)
116+
117+
if datetime:
118+
datetime_search = self._return_date(datetime)
119+
search = self.database.apply_datetime_filter(
120+
search=search, datetime_search=datetime_search
121+
)
122+
123+
if bbox:
124+
bbox = [float(x) for x in bbox]
125+
if len(bbox) == 6:
126+
bbox = [bbox[0], bbox[1], bbox[3], bbox[4]]
127+
128+
search = self.database.apply_bbox_filter(search=search, bbox=bbox)
99129

100130
items, maybe_count, next_token = await self.database.execute_search(
101-
search=self.database.apply_collections_filter(
102-
self.database.make_search(), [collection_id]
103-
),
131+
search=search,
104132
limit=limit,
105-
token=token,
106133
sort=None,
134+
token=token, # type: ignore
107135
collection_ids=[collection_id],
108-
ignore_unavailable=False,
109136
)
110137

111138
items = [
@@ -236,7 +263,7 @@ async def get_search(
236263

237264
@overrides
238265
async def post_search(
239-
self, search_request: stac_pydantic.api.Search, **kwargs
266+
self, search_request: BaseSearchPostRequest, **kwargs
240267
) -> ItemCollection:
241268
"""POST search catalog."""
242269
request: Request = kwargs["request"]
@@ -280,14 +307,15 @@ async def post_search(
280307
search=search, op=op, field=field, value=value
281308
)
282309

283-
filter_lang = getattr(search_request, "filter_lang", None)
284-
310+
# only cql2_json is supported here
285311
if hasattr(search_request, "filter"):
286312
cql2_filter = getattr(search_request, "filter", None)
287-
if filter_lang in [None, FilterLang.cql2_json]:
313+
try:
288314
search = self.database.apply_cql2_filter(search, cql2_filter)
289-
else:
290-
raise Exception("CQL2-Text is not supported with POST")
315+
except Exception as e:
316+
raise HTTPException(
317+
status_code=400, detail=f"Error with cql2_json filter: {e}"
318+
)
291319

292320
sort = None
293321
if search_request.sortby:
@@ -358,7 +386,9 @@ class TransactionsClient(AsyncBaseTransactionsClient):
358386
database = DatabaseLogic()
359387

360388
@overrides
361-
async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
389+
async def create_item(
390+
self, collection_id: str, item: stac_types.Item, **kwargs
391+
) -> stac_types.Item:
362392
"""Create item."""
363393
base_url = str(kwargs["request"].base_url)
364394

@@ -369,8 +399,6 @@ async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
369399
bulk_client.preprocess_item(item, base_url) for item in item["features"] # type: ignore
370400
]
371401

372-
# not a great way to get the collection_id-- should be part of the method signature
373-
collection_id = processed_items[0]["collection"]
374402
await self.database.bulk_async(
375403
collection_id, processed_items, refresh=kwargs.get("refresh", False)
376404
)
@@ -382,18 +410,19 @@ async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
382410
return item
383411

384412
@overrides
385-
async def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
413+
async def update_item(
414+
self, collection_id: str, item_id: str, item: stac_types.Item, **kwargs
415+
) -> stac_types.Item:
386416
"""Update item."""
387417
base_url = str(kwargs["request"].base_url)
388-
collection_id = item["collection"]
389418

390419
now = datetime_type.now(timezone.utc).isoformat().replace("+00:00", "Z")
391420
item["properties"]["updated"] = str(now)
392421

393422
await self.database.check_collection_exists(collection_id)
394423
# todo: index instead of delete and create
395-
await self.delete_item(item_id=item["id"], collection_id=collection_id)
396-
await self.create_item(item=item, **kwargs)
424+
await self.delete_item(item_id=item_id, collection_id=collection_id)
425+
await self.create_item(collection_id=collection_id, item=item, **kwargs)
397426

398427
return ItemSerializer.db_to_stac(item, base_url)
399428

stac_fastapi/elasticsearch/tests/api/test_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"POST /collections",
3030
"POST /collections/{collection_id}/items",
3131
"PUT /collections",
32-
"PUT /collections/{collection_id}/items",
32+
"PUT /collections/{collection_id}/items/{item_id}",
3333
}
3434

3535

stac_fastapi/elasticsearch/tests/clients/test_elasticsearch.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ async def test_get_collection_items(app_client, ctx, core_client, txn_client):
9292
for _ in range(num_of_items_to_create):
9393
item = deepcopy(ctx.item)
9494
item["id"] = str(uuid.uuid4())
95-
await txn_client.create_item(item, request=MockRequest, refresh=True)
95+
await txn_client.create_item(
96+
collection_id=item["collection"],
97+
item=item,
98+
request=MockRequest,
99+
refresh=True,
100+
)
96101

97102
fc = await core_client.item_collection(coll["id"], request=MockRequest())
98103
assert len(fc["features"]) == num_of_items_to_create + 1 # ctx.item
@@ -112,15 +117,24 @@ async def test_create_item(ctx, core_client, txn_client):
112117

113118
async def test_create_item_already_exists(ctx, txn_client):
114119
with pytest.raises(ConflictError):
115-
await txn_client.create_item(ctx.item, request=MockRequest, refresh=True)
120+
await txn_client.create_item(
121+
collection_id=ctx.item["collection"],
122+
item=ctx.item,
123+
request=MockRequest,
124+
refresh=True,
125+
)
116126

117127

118128
async def test_update_item(ctx, core_client, txn_client):
119129
ctx.item["properties"]["foo"] = "bar"
120-
await txn_client.update_item(ctx.item, request=MockRequest)
130+
collection_id = ctx.item["collection"]
131+
item_id = ctx.item["id"]
132+
await txn_client.update_item(
133+
collection_id=collection_id, item_id=item_id, item=ctx.item, request=MockRequest
134+
)
121135

122136
updated_item = await core_client.get_item(
123-
ctx.item["id"], ctx.item["collection"], request=MockRequest
137+
item_id, collection_id, request=MockRequest
124138
)
125139
assert updated_item["properties"]["foo"] == "bar"
126140

@@ -137,10 +151,14 @@ async def test_update_geometry(ctx, core_client, txn_client):
137151
]
138152

139153
ctx.item["geometry"]["coordinates"] = new_coordinates
140-
await txn_client.update_item(ctx.item, request=MockRequest)
154+
collection_id = ctx.item["collection"]
155+
item_id = ctx.item["id"]
156+
await txn_client.update_item(
157+
collection_id=collection_id, item_id=item_id, item=ctx.item, request=MockRequest
158+
)
141159

142160
updated_item = await core_client.get_item(
143-
ctx.item["id"], ctx.item["collection"], request=MockRequest
161+
item_id, collection_id, request=MockRequest
144162
)
145163
assert updated_item["geometry"]["coordinates"] == new_coordinates
146164

stac_fastapi/elasticsearch/tests/conftest.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,20 @@ async def create_collection(txn_client: TransactionsClient, collection: Dict) ->
9494

9595

9696
async def create_item(txn_client: TransactionsClient, item: Dict) -> None:
97-
await txn_client.create_item(item, request=MockRequest, refresh=True)
97+
if "collection" in item:
98+
await txn_client.create_item(
99+
collection_id=item["collection"],
100+
item=item,
101+
request=MockRequest,
102+
refresh=True,
103+
)
104+
else:
105+
await txn_client.create_item(
106+
collection_id=item["features"][0]["collection"],
107+
item=item,
108+
request=MockRequest,
109+
refresh=True,
110+
)
98111

99112

100113
async def delete_collections_and_items(txn_client: TransactionsClient) -> None:

stac_fastapi/elasticsearch/tests/resources/test_collection.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ async def test_create_and_delete_collection(app_client, load_test_data):
1010
assert resp.status_code == 200
1111

1212
resp = await app_client.delete(f"/collections/{test_collection['id']}")
13-
assert resp.status_code == 200
13+
assert resp.status_code == 204
1414

1515

1616
async def test_create_collection_conflict(app_client, ctx):

0 commit comments

Comments
 (0)