Skip to content

Commit 77b368f

Browse files
change transactions clients method signatures to match use, allow result of None to return 204 (#387)
Co-authored-by: Nathan Zimmerman <[email protected]>
1 parent 43f72f4 commit 77b368f

File tree

5 files changed

+80
-47
lines changed

5 files changed

+80
-47
lines changed

stac_fastapi/api/stac_fastapi/api/routes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
from starlette.requests import Request
88
from starlette.responses import JSONResponse, Response
99
from starlette.routing import BaseRoute, Match
10+
from starlette.status import HTTP_204_NO_CONTENT
1011

1112
from stac_fastapi.api.models import APIRequest
1213

1314

1415
def _wrap_response(resp: Any, response_class: Type[Response]) -> Response:
1516
if isinstance(resp, Response):
1617
return resp
17-
else:
18+
elif resp is not None:
1819
return response_class(resp)
20+
else: # None is returned as 204 No Content
21+
return Response(status_code=HTTP_204_NO_CONTENT)
1922

2023

2124
def create_async_endpoint(

stac_fastapi/pgstac/stac_fastapi/pgstac/core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def get_collection(self, collection_id: str, **kwargs) -> Collection:
7777
Called with `GET /collections/{collection_id}`.
7878
7979
Args:
80-
id: Id of the collection.
80+
collection_id: ID of the collection.
8181
8282
Returns:
8383
Collection.
@@ -212,7 +212,8 @@ async def get_item(self, item_id: str, collection_id: str, **kwargs) -> Item:
212212
Called with `GET /collections/{collection_id}/items/{item_id}`.
213213
214214
Args:
215-
id: Id of the item.
215+
item_id: ID of the item.
216+
collection_id: ID of the collection the item is in.
216217
217218
Returns:
218219
Item.
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""transactions extension client."""
22

33
import logging
4-
from typing import Dict
4+
from typing import Optional, Union
55

66
import attr
7+
from starlette.responses import JSONResponse, Response
78

89
from stac_fastapi.pgstac.db import dbfunc
910
from stac_fastapi.types import stac as stac_types
@@ -17,14 +18,18 @@
1718
class TransactionsClient(AsyncBaseTransactionsClient):
1819
"""Transactions extension specific CRUD operations."""
1920

20-
async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
21+
async def create_item(
22+
self, item: stac_types.Item, **kwargs
23+
) -> Optional[Union[stac_types.Item, Response]]:
2124
"""Create item."""
2225
request = kwargs["request"]
2326
pool = request.app.state.writepool
2427
await dbfunc(pool, "create_item", item)
2528
return item
2629

27-
async def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
30+
async def update_item(
31+
self, item: stac_types.Item, **kwargs
32+
) -> Optional[Union[stac_types.Item, Response]]:
2833
"""Update item."""
2934
request = kwargs["request"]
3035
pool = request.app.state.writepool
@@ -33,7 +38,7 @@ async def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
3338

3439
async def create_collection(
3540
self, collection: stac_types.Collection, **kwargs
36-
) -> stac_types.Collection:
41+
) -> Optional[Union[stac_types.Collection, Response]]:
3742
"""Create collection."""
3843
request = kwargs["request"]
3944
pool = request.app.state.writepool
@@ -42,23 +47,27 @@ async def create_collection(
4247

4348
async def update_collection(
4449
self, collection: stac_types.Collection, **kwargs
45-
) -> stac_types.Collection:
50+
) -> Optional[Union[stac_types.Collection, Response]]:
4651
"""Update collection."""
4752
request = kwargs["request"]
4853
pool = request.app.state.writepool
4954
await dbfunc(pool, "update_collection", collection)
5055
return collection
5156

52-
async def delete_item(self, item_id: str, collection_id: str, **kwargs) -> Dict:
53-
"""Delete collection."""
57+
async def delete_item(
58+
self, item_id: str, **kwargs
59+
) -> Optional[Union[stac_types.Item, Response]]:
60+
"""Delete item."""
5461
request = kwargs["request"]
5562
pool = request.app.state.writepool
5663
await dbfunc(pool, "delete_item", item_id)
57-
return {"deleted item": item_id}
64+
return JSONResponse({"deleted item": item_id})
5865

59-
async def delete_collection(self, collection_id: str, **kwargs) -> Dict:
66+
async def delete_collection(
67+
self, collection_id: str, **kwargs
68+
) -> Optional[Union[stac_types.Collection, Response]]:
6069
"""Delete collection."""
6170
request = kwargs["request"]
6271
pool = request.app.state.writepool
6372
await dbfunc(pool, "delete_collection", collection_id)
64-
return {"deleted collection": collection_id}
73+
return JSONResponse({"deleted collection": collection_id})

stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/transactions.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""transactions extension client."""
22

33
import logging
4-
from typing import Optional, Type
4+
from typing import Optional, Type, Union
55

66
import attr
7+
from starlette.responses import Response
78

89
from stac_fastapi.extensions.third_party.bulk_transactions import (
910
BaseBulkTransactionsClient,
@@ -33,64 +34,68 @@ class TransactionsClient(BaseTransactionsClient):
3334
default=serializers.CollectionSerializer
3435
)
3536

36-
def create_item(self, model: stac_types.Item, **kwargs) -> stac_types.Item:
37+
def create_item(
38+
self, item: stac_types.Item, **kwargs
39+
) -> Optional[Union[stac_types.Item, Response]]:
3740
"""Create item."""
3841
base_url = str(kwargs["request"].base_url)
39-
data = self.item_serializer.stac_to_db(model)
42+
data = self.item_serializer.stac_to_db(item)
4043
with self.session.writer.context_session() as session:
4144
session.add(data)
4245
return self.item_serializer.db_to_stac(data, base_url)
4346

4447
def create_collection(
45-
self, model: stac_types.Collection, **kwargs
46-
) -> stac_types.Collection:
48+
self, collection: stac_types.Collection, **kwargs
49+
) -> Optional[Union[stac_types.Collection, Response]]:
4750
"""Create collection."""
4851
base_url = str(kwargs["request"].base_url)
49-
data = self.collection_serializer.stac_to_db(model)
52+
data = self.collection_serializer.stac_to_db(collection)
5053
with self.session.writer.context_session() as session:
5154
session.add(data)
5255
return self.collection_serializer.db_to_stac(data, base_url=base_url)
5356

54-
def update_item(self, model: stac_types.Item, **kwargs) -> stac_types.Item:
57+
def update_item(
58+
self, item: stac_types.Item, **kwargs
59+
) -> Optional[Union[stac_types.Item, Response]]:
5560
"""Update item."""
5661
base_url = str(kwargs["request"].base_url)
5762
with self.session.reader.context_session() as session:
5863
query = session.query(self.item_table).filter(
59-
self.item_table.id == model["id"]
64+
self.item_table.id == item["id"]
6065
)
61-
query = query.filter(self.item_table.collection_id == model["collection"])
66+
query = query.filter(self.item_table.collection_id == item["collection"])
6267
if not query.scalar():
6368
raise NotFoundError(
64-
f"Item {model['id']} in collection {model['collection']}"
69+
f"Item {item['id']} in collection {item['collection']}"
6570
)
6671
# SQLAlchemy orm updates don't seem to like geoalchemy types
67-
db_model = self.item_serializer.stac_to_db(model)
72+
db_model = self.item_serializer.stac_to_db(item)
6873
query.update(self.item_serializer.row_to_dict(db_model))
6974
stac_item = self.item_serializer.db_to_stac(db_model, base_url)
7075

7176
return stac_item
7277

7378
def update_collection(
74-
self, model: stac_types.Collection, **kwargs
75-
) -> stac_types.Collection:
79+
self, collection: stac_types.Collection, **kwargs
80+
) -> Optional[Union[stac_types.Collection, Response]]:
7681
"""Update collection."""
7782
base_url = str(kwargs["request"].base_url)
7883
with self.session.reader.context_session() as session:
7984
query = session.query(self.collection_table).filter(
80-
self.collection_table.id == model["id"]
85+
self.collection_table.id == collection["id"]
8186
)
8287
if not query.scalar():
83-
raise NotFoundError(f"Item {model['id']} not found")
88+
raise NotFoundError(f"Item {collection['id']} not found")
8489

8590
# SQLAlchemy orm updates don't seem to like geoalchemy types
86-
db_model = self.collection_serializer.stac_to_db(model)
91+
db_model = self.collection_serializer.stac_to_db(collection)
8792
query.update(self.collection_serializer.row_to_dict(db_model))
8893

8994
return self.collection_serializer.db_to_stac(db_model, base_url)
9095

9196
def delete_item(
9297
self, item_id: str, collection_id: str, **kwargs
93-
) -> stac_types.Item:
98+
) -> Optional[Union[stac_types.Item, Response]]:
9499
"""Delete item."""
95100
base_url = str(kwargs["request"].base_url)
96101
with self.session.writer.context_session() as session:
@@ -106,7 +111,9 @@ def delete_item(
106111
query.delete()
107112
return self.item_serializer.db_to_stac(data, base_url=base_url)
108113

109-
def delete_collection(self, collection_id: str, **kwargs) -> stac_types.Collection:
114+
def delete_collection(
115+
self, collection_id: str, **kwargs
116+
) -> Optional[Union[stac_types.Collection, Response]]:
110117
"""Delete collection."""
111118
base_url = str(kwargs["request"].base_url)
112119
with self.session.writer.context_session() as session:

stac_fastapi/types/stac_fastapi/types/core.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
import attr
88
from fastapi import Request
9-
from stac_pydantic.api import Search
109
from stac_pydantic.links import Relations
1110
from stac_pydantic.shared import MimeTypes
1211
from stac_pydantic.version import STAC_VERSION
12+
from starlette.responses import Response
1313

1414
from stac_fastapi.types import stac as stac_types
1515
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
@@ -23,16 +23,19 @@
2323

2424
@attr.s # type:ignore
2525
class BaseTransactionsClient(abc.ABC):
26-
"""Defines a pattern for implementing the STAC transaction extension."""
26+
"""Defines a pattern for implementing the STAC API Transaction Extension."""
2727

2828
@abc.abstractmethod
29-
def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
29+
def create_item(
30+
self, item: stac_types.Item, **kwargs
31+
) -> Optional[Union[stac_types.Item, Response]]:
3032
"""Create a new item.
3133
3234
Called with `POST /collections/{collection_id}/items`.
3335
3436
Args:
3537
item: the item
38+
collection_id: the id of the collection from the resource path
3639
3740
Returns:
3841
The item that was created.
@@ -41,7 +44,9 @@ def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
4144
...
4245

4346
@abc.abstractmethod
44-
def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
47+
def update_item(
48+
self, item: stac_types.Item, **kwargs
49+
) -> Optional[Union[stac_types.Item, Response]]:
4550
"""Perform a complete update on an existing item.
4651
4752
Called with `PUT /collections/{collection_id}/items`. It is expected that this item already exists. The update
@@ -50,6 +55,7 @@ def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
5055
5156
Args:
5257
item: the item (must be complete)
58+
collection_id: the id of the collection from the resource path
5359
5460
Returns:
5561
The updated item.
@@ -59,7 +65,7 @@ def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
5965
@abc.abstractmethod
6066
def delete_item(
6167
self, item_id: str, collection_id: str, **kwargs
62-
) -> stac_types.Item:
68+
) -> Optional[Union[stac_types.Item, Response]]:
6369
"""Delete an item from a collection.
6470
6571
Called with `DELETE /collections/{collection_id}/items/{item_id}`
@@ -76,7 +82,7 @@ def delete_item(
7682
@abc.abstractmethod
7783
def create_collection(
7884
self, collection: stac_types.Collection, **kwargs
79-
) -> stac_types.Collection:
85+
) -> Optional[Union[stac_types.Collection, Response]]:
8086
"""Create a new collection.
8187
8288
Called with `POST /collections`.
@@ -92,7 +98,7 @@ def create_collection(
9298
@abc.abstractmethod
9399
def update_collection(
94100
self, collection: stac_types.Collection, **kwargs
95-
) -> stac_types.Collection:
101+
) -> Optional[Union[stac_types.Collection, Response]]:
96102
"""Perform a complete update on an existing collection.
97103
98104
Called with `PUT /collections`. It is expected that this item already exists. The update should do a diff
@@ -101,14 +107,17 @@ def update_collection(
101107
102108
Args:
103109
collection: the collection (must be complete)
110+
collection_id: the id of the collection from the resource path
104111
105112
Returns:
106113
The updated collection.
107114
"""
108115
...
109116

110117
@abc.abstractmethod
111-
def delete_collection(self, collection_id: str, **kwargs) -> stac_types.Collection:
118+
def delete_collection(
119+
self, collection_id: str, **kwargs
120+
) -> Optional[Union[stac_types.Collection, Response]]:
112121
"""Delete a collection.
113122
114123
Called with `DELETE /collections/{collection_id}`
@@ -127,7 +136,9 @@ class AsyncBaseTransactionsClient(abc.ABC):
127136
"""Defines a pattern for implementing the STAC transaction extension."""
128137

129138
@abc.abstractmethod
130-
async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
139+
async def create_item(
140+
self, item: stac_types.Item, **kwargs
141+
) -> Optional[Union[stac_types.Item, Response]]:
131142
"""Create a new item.
132143
133144
Called with `POST /collections/{collection_id}/items`.
@@ -142,7 +153,9 @@ async def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
142153
...
143154

144155
@abc.abstractmethod
145-
async def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
156+
async def update_item(
157+
self, item: stac_types.Item, **kwargs
158+
) -> Optional[Union[stac_types.Item, Response]]:
146159
"""Perform a complete update on an existing item.
147160
148161
Called with `PUT /collections/{collection_id}/items`. It is expected that this item already exists. The update
@@ -160,7 +173,7 @@ async def update_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item:
160173
@abc.abstractmethod
161174
async def delete_item(
162175
self, item_id: str, collection_id: str, **kwargs
163-
) -> stac_types.Item:
176+
) -> Optional[Union[stac_types.Item, Response]]:
164177
"""Delete an item from a collection.
165178
166179
Called with `DELETE /collections/{collection_id}/items/{item_id}`
@@ -177,7 +190,7 @@ async def delete_item(
177190
@abc.abstractmethod
178191
async def create_collection(
179192
self, collection: stac_types.Collection, **kwargs
180-
) -> stac_types.Collection:
193+
) -> Optional[Union[stac_types.Collection, Response]]:
181194
"""Create a new collection.
182195
183196
Called with `POST /collections`.
@@ -193,7 +206,7 @@ async def create_collection(
193206
@abc.abstractmethod
194207
async def update_collection(
195208
self, collection: stac_types.Collection, **kwargs
196-
) -> stac_types.Collection:
209+
) -> Optional[Union[stac_types.Collection, Response]]:
197210
"""Perform a complete update on an existing collection.
198211
199212
Called with `PUT /collections`. It is expected that this item already exists. The update should do a diff
@@ -211,7 +224,7 @@ async def update_collection(
211224
@abc.abstractmethod
212225
async def delete_collection(
213226
self, collection_id: str, **kwargs
214-
) -> stac_types.Collection:
227+
) -> Optional[Union[stac_types.Collection, Response]]:
215228
"""Delete a collection.
216229
217230
Called with `DELETE /collections/{collection_id}`
@@ -394,7 +407,7 @@ def conformance(self, **kwargs) -> stac_types.Conformance:
394407

395408
@abc.abstractmethod
396409
def post_search(
397-
self, search_request: Search, **kwargs
410+
self, search_request: BaseSearchPostRequest, **kwargs
398411
) -> stac_types.ItemCollection:
399412
"""Cross catalog search (POST).
400413
@@ -581,7 +594,7 @@ async def conformance(self, **kwargs) -> stac_types.Conformance:
581594

582595
@abc.abstractmethod
583596
async def post_search(
584-
self, search_request: Search, **kwargs
597+
self, search_request: BaseSearchPostRequest, **kwargs
585598
) -> stac_types.ItemCollection:
586599
"""Cross catalog search (POST).
587600

0 commit comments

Comments
 (0)