Skip to content

Commit 78ed465

Browse files
[PT-5400] add bulk deletion (#762)
--------- Co-authored-by: Matheus Pinheiro <[email protected]>
1 parent 809d540 commit 78ed465

File tree

5 files changed

+90
-1
lines changed

5 files changed

+90
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ You can check your current version with the following command:
3030

3131
For more information, see [UP42 Python package description](https://pypi.org/project/up42-py/).
3232

33+
### 2.5.0a4
34+
**August 6, 2025**
35+
- Added bulk deletion of items: `BulkDeletion`.
3336

3437
### 2.5.0a3
3538
**Aug 4, 2025**

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "up42-py"
3-
version = "2.5.0a3"
3+
version = "2.5.0a4"
44
description = "Python SDK for UP42, the geospatial marketplace and developer platform."
55
authors = ["UP42 GmbH <[email protected]>"]
66
license = "https://github.com/up42/up42-py/blob/master/LICENSE"

tests/test_stac.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime as dt
22
import uuid
3+
from unittest import mock
34

45
import pystac
56
import pytest
@@ -143,3 +144,49 @@ def test_should_set_up42_extension(self, entity, entity_dict, attribute, key):
143144
new_value = "new-value"
144145
setattr(entity.up42, attribute, new_value) # type: ignore
145146
assert entity_dict[key] == new_value
147+
148+
149+
class TestBulkDeletion:
150+
items = [
151+
pystac.Item(
152+
id=f"item-id-{i}",
153+
collection="collection-id",
154+
geometry=None,
155+
bbox=None,
156+
datetime=dt.datetime.now(),
157+
properties={},
158+
)
159+
for i in range(2)
160+
]
161+
collection_with_all_items = pystac.Collection(
162+
id="collection-id",
163+
description="",
164+
extent=pystac.Extent(
165+
spatial=pystac.SpatialExtent(bboxes=[[1.0, 2.0, 3.0, 4.0]]),
166+
temporal=pystac.TemporalExtent(intervals=[[dt.datetime.now(), None]]),
167+
),
168+
extra_fields={},
169+
)
170+
collection_with_all_items.add_items(items)
171+
172+
def test_should_raise_and_not_submit_when_missing_items(self):
173+
mock_stac_client = mock.Mock()
174+
mock_stac_client.get_items.return_value = iter([self.items[0]])
175+
bulk_deletion = stac.BulkDeletion(self.items[0].id)
176+
bulk_deletion.stac_client = mock_stac_client
177+
with pytest.raises(stac.IncompleteCollectionDeletionError):
178+
bulk_deletion.delete()
179+
180+
def test_should_delete_staged_items(self, requests_mock: req_mock.Mocker):
181+
requests_mock.delete(
182+
url=f"/v2/assets/stac/collections/{self.collection_with_all_items.id}",
183+
status_code=204,
184+
)
185+
mock_stac_client = mock.Mock()
186+
mock_stac_client.get_items.return_value = iter(self.items)
187+
188+
bulk_deletion = stac.BulkDeletion(self.items[0].id, self.items[1].id)
189+
bulk_deletion.stac_client = mock_stac_client
190+
191+
bulk_deletion.delete()
192+
assert requests_mock.request_history[0].method == "DELETE"

up42/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from up42.order import Order, OrderSorting
3535
from up42.order_template import BatchOrderTemplate
3636
from up42.processing import Job, JobSorting, JobStatus
37+
from up42.stac import BulkDeletion
3738
from up42.stac import extend as stac_extend
3839
from up42.storage import Storage
3940
from up42.tasking import FeasibilityStudy, FeasibilityStudySorting, Quotation, QuotationSorting, Tasking
@@ -83,5 +84,6 @@
8384
QuotationSorting,
8485
FeasibilityStudy,
8586
FeasibilityStudySorting,
87+
BulkDeletion,
8688
]
8789
]

up42/stac.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ class InvalidUp42Asset(ValueError):
99
pass
1010

1111

12+
class IncompleteCollectionDeletionError(ValueError):
13+
"""Raised when attempting to delete a collection but not all items in the collection are included."""
14+
15+
pass
16+
17+
1218
class FileProvider:
1319
session = base.Session()
1420

@@ -96,3 +102,34 @@ def extend():
96102

97103
pystac.Item.up42 = Up42ExtensionProvider() # type: ignore
98104
pystac.Collection.up42 = Up42ExtensionProvider() # type: ignore
105+
106+
107+
class BulkDeletion:
108+
session = base.Session()
109+
stac_client = base.StacClient()
110+
111+
def __init__(self, *item_ids: str):
112+
self._item_ids = set(item_ids)
113+
114+
def delete(self):
115+
items = self.stac_client.get_items(*self._item_ids)
116+
collections: set[pystac.Collection] = set()
117+
for item in items:
118+
collection = item.get_parent()
119+
collections.add(collection) # type: ignore
120+
121+
for collection in collections:
122+
collection_item_ids = {item_in_collection.id for item_in_collection in collection.get_items()}
123+
missing_items = collection_item_ids - self._item_ids
124+
if missing_items:
125+
error_msg = (
126+
f"The deletion request failed because the submitted items are part of a group that must be deleted "
127+
f"together. The submitted items belong to the following collection: '{collection.id}'. All items "
128+
f"from this collection must be deleted at once. To proceed, please add these missing item IDs "
129+
f"to your request: {list(missing_items)}."
130+
)
131+
raise IncompleteCollectionDeletionError(error_msg)
132+
133+
for collection in collections:
134+
url = host.endpoint(f"/v2/assets/stac/collections/{collection.id}")
135+
self.session.delete(url=url)

0 commit comments

Comments
 (0)