Skip to content

Commit 108fc2c

Browse files
avbentemArjan van Bentemjonhealy1
authored
Fix PUT collections (#666)
Co-authored-by: Arjan van Bentem <[email protected]> Co-authored-by: Jonathan Healy <[email protected]>
1 parent 0bd592e commit 108fc2c

File tree

5 files changed

+89
-22
lines changed

5 files changed

+89
-22
lines changed

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
### Fixed
66

7+
* Fix missing payload for the PUT `collection/{collection_id}` endpoint ([#665](https://github.com/stac-utils/stac-fastapi/issues/665))
78
* Return 400 for datetime errors ([#670](https://github.com/stac-utils/stac-fastapi/pull/670))
89

910
## [2.5.3] - 2024-04-23
1011

11-
### Fixed
12+
### Fixed
1213

1314
* Remove the str2list converter from intersection queries via BaseSearchGetRequest ([#668](https://github.com/stac-utils/stac-fastapi/pull/668))
1415
* Apply datetime converter in ItemCollection endpoint model ([#667](https://github.com/stac-utils/stac-fastapi/pull/667))

stac_fastapi/api/stac_fastapi/api/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@ def create_post_request_model(
103103

104104
@attr.s # type:ignore
105105
class CollectionUri(APIRequest):
106-
"""Delete collection."""
106+
"""Get or delete collection."""
107107

108108
collection_id: str = attr.ib(default=Path(..., description="Collection ID"))
109109

110110

111111
@attr.s
112112
class ItemUri(CollectionUri):
113-
"""Delete item."""
113+
"""Get or delete item."""
114114

115115
item_id: str = attr.ib(default=Path(..., description="Item ID"))
116116

stac_fastapi/extensions/stac_fastapi/extensions/core/transaction.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ class PutItem(ItemUri):
3131
item: stac_types.Item = attr.ib(default=Body(None))
3232

3333

34+
@attr.s
35+
class PutCollection(CollectionUri):
36+
"""Update Collection."""
37+
38+
collection: stac_types.Collection = attr.ib(default=Body(None))
39+
40+
3441
@attr.s
3542
class TransactionExtension(ApiExtension):
3643
"""Transaction Extension.
@@ -128,7 +135,7 @@ def register_update_collection(self):
128135
response_model_exclude_none=True,
129136
methods=["PUT"],
130137
endpoint=create_async_endpoint(
131-
self.client.update_collection, stac_types.Collection
138+
self.client.update_collection, PutCollection
132139
),
133140
)
134141

stac_fastapi/extensions/tests/test_transaction.py

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from stac_fastapi.extensions.core import TransactionExtension
99
from stac_fastapi.types.config import ApiSettings
1010
from stac_fastapi.types.core import BaseCoreClient, BaseTransactionsClient
11-
from stac_fastapi.types.stac import Item, ItemCollection
11+
from stac_fastapi.types.stac import Collection, Item, ItemCollection
1212

1313

1414
class DummyCoreClient(BaseCoreClient):
@@ -32,25 +32,32 @@ def item_collection(self, *args, **kwargs):
3232

3333

3434
class DummyTransactionsClient(BaseTransactionsClient):
35-
"""Defines a pattern for implementing the STAC transaction extension."""
35+
"""Dummy client returning parts of the request, rather than proper STAC items."""
3636

37-
def create_item(self, item: Union[Item, ItemCollection], *args, **kwargs):
38-
return {"created": True, "type": item["type"]}
37+
def create_item(self, item: Union[Item, ItemCollection], **kwargs):
38+
return {"type": item["type"]}
3939

40-
def update_item(self, *args, **kwargs):
41-
raise NotImplementedError
40+
def update_item(self, collection_id: str, item_id: str, item: Item, **kwargs):
41+
return {
42+
"path_collection_id": collection_id,
43+
"path_item_id": item_id,
44+
"type": item["type"],
45+
}
4246

43-
def delete_item(self, *args, **kwargs):
44-
raise NotImplementedError
47+
def delete_item(self, item_id: str, collection_id: str, **kwargs):
48+
return {
49+
"path_collection_id": collection_id,
50+
"path_item_id": item_id,
51+
}
4552

46-
def create_collection(self, *args, **kwargs):
47-
raise NotImplementedError
53+
def create_collection(self, collection: Collection, **kwargs):
54+
return {"type": collection["type"]}
4855

49-
def update_collection(self, *args, **kwargs):
50-
raise NotImplementedError
56+
def update_collection(self, collection_id: str, collection: Collection, **kwargs):
57+
return {"path_collection_id": collection_id, "type": collection["type"]}
5158

52-
def delete_collection(self, *args, **kwargs):
53-
raise NotImplementedError
59+
def delete_collection(self, collection_id: str, **kwargs):
60+
return {"path_collection_id": collection_id}
5461

5562

5663
def test_create_item(client: TestClient, item: Item) -> None:
@@ -69,6 +76,42 @@ def test_create_item_collection(
6976
assert response.json()["type"] == "FeatureCollection"
7077

7178

79+
def test_update_item(client: TestClient, item: Item) -> None:
80+
response = client.put(
81+
"/collections/a-collection/items/an-item", content=json.dumps(item)
82+
)
83+
assert response.is_success, response.text
84+
assert response.json()["path_collection_id"] == "a-collection"
85+
assert response.json()["path_item_id"] == "an-item"
86+
assert response.json()["type"] == "Feature"
87+
88+
89+
def test_delete_item(client: TestClient) -> None:
90+
response = client.delete("/collections/a-collection/items/an-item")
91+
assert response.is_success, response.text
92+
assert response.json()["path_collection_id"] == "a-collection"
93+
assert response.json()["path_item_id"] == "an-item"
94+
95+
96+
def test_create_collection(client: TestClient, collection: Collection) -> None:
97+
response = client.post("/collections", content=json.dumps(collection))
98+
assert response.is_success, response.text
99+
assert response.json()["type"] == "Collection"
100+
101+
102+
def test_update_collection(client: TestClient, collection: Collection) -> None:
103+
response = client.put("/collections/a-collection", content=json.dumps(collection))
104+
assert response.is_success, response.text
105+
assert response.json()["path_collection_id"] == "a-collection"
106+
assert response.json()["type"] == "Collection"
107+
108+
109+
def test_delete_collection(client: TestClient, collection: Collection) -> None:
110+
response = client.delete("/collections/a-collection")
111+
assert response.is_success, response.text
112+
assert response.json()["path_collection_id"] == "a-collection"
113+
114+
72115
@pytest.fixture
73116
def client(
74117
core_client: DummyCoreClient, transactions_client: DummyTransactionsClient
@@ -119,3 +162,19 @@ def item() -> Item:
119162
"assets": {},
120163
"collection": "test_collection",
121164
}
165+
166+
167+
@pytest.fixture
168+
def collection() -> Collection:
169+
return {
170+
"type": "Collection",
171+
"stac_version": "1.0.0",
172+
"stac_extensions": [],
173+
"id": "test_collection",
174+
"extent": {
175+
"spatial": {"bbox": [[-180, -90, 180, 90]]},
176+
"temporal": {
177+
"interval": [["2000-01-01T00:00:00Z", "2024-01-01T00:00:00Z"]]
178+
},
179+
},
180+
}

stac_fastapi/types/stac_fastapi/types/core.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ def update_collection(
109109
) -> Optional[Union[stac_types.Collection, Response]]:
110110
"""Perform a complete update on an existing collection.
111111
112-
Called with `PUT /collections/{collection_id}`. It is expected that this item
113-
already exists. The update should do a diff against the saved collection and
114-
perform any necessary updates. Partial updates are not supported by the
115-
transactions extension.
112+
Called with `PUT /collections/{collection_id}`. It is expected that this
113+
collection already exists. The update should do a diff against the saved
114+
collection and perform any necessary updates. Partial updates are not
115+
supported by the transactions extension.
116116
117117
Args:
118118
collection_id: id of the existing collection to be updated

0 commit comments

Comments
 (0)