Skip to content

Commit

Permalink
Allow to omit collection in bulk insertions (#113)
Browse files Browse the repository at this point in the history
* In bulk insertions allow to omit collection in items
Same checks in item bulk insertions as in single insertions
Added tests

* Fixing whitespace

* Update changelog

* Linting fix

* fix tests

---------

Co-authored-by: vincentsarago <[email protected]>
  • Loading branch information
constantinius and vincentsarago authored Aug 2, 2024
1 parent 1f36485 commit cb08b0f
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

- Enable filter extension for `GET /items` requests and add `Queryables` links in `/collections` and `/collections/{collection_id}` responses ([#89](https://github.com/stac-utils/stac-fastapi-pgstac/pull/89))
- Allow to omit `collection` in bulk item insertions. Same identifier checks as with single insertions ([#113](https://github.com/stac-utils/stac-fastapi-pgstac/pull/113))

## [3.0.0a4] - 2024-07-10

Expand Down
18 changes: 13 additions & 5 deletions stac_fastapi/pgstac/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
logger.setLevel(logging.INFO)


@attr.s
class TransactionsClient(AsyncBaseTransactionsClient):
"""Transactions extension specific CRUD operations."""

class ClientValidateMixIn:
def _validate_id(self, id: str, settings: Settings):
invalid_chars = settings.invalid_id_chars
id_regex = "[" + "".join(re.escape(char) for char in invalid_chars) + "]"
Expand Down Expand Up @@ -67,6 +64,11 @@ def _validate_item(
detail=f"Item ID from path parameter ({expected_item_id}) does not match Item ID from Item ({body_item_id})",
)


@attr.s
class TransactionsClient(AsyncBaseTransactionsClient, ClientValidateMixIn):
"""Transactions extension specific CRUD operations."""

async def create_item(
self,
collection_id: str,
Expand Down Expand Up @@ -203,11 +205,17 @@ async def delete_collection(


@attr.s
class BulkTransactionsClient(AsyncBaseBulkTransactionsClient):
class BulkTransactionsClient(AsyncBaseBulkTransactionsClient, ClientValidateMixIn):
"""Postgres bulk transactions."""

async def bulk_item_insert(self, items: Items, request: Request, **kwargs) -> str:
"""Bulk item insertion using pgstac."""
collection_id = request.path_params["collection_id"]

for item_id, item in items.items.items():
self._validate_item(request, item, collection_id, item_id)
item["collection"] = collection_id

items_to_insert = list(items.items.values())

async with request.app.state.get_connection(request, "w") as conn:
Expand Down
90 changes: 90 additions & 0 deletions tests/clients/test_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,96 @@ async def test_create_bulk_items_already_exist_upsert(
assert resp.text == '"Successfully upserted 2 items."'


async def test_create_bulk_items_omit_collection(
app_client, load_test_data: Callable, load_test_collection
):
coll = load_test_collection
item = load_test_data("test_item.json")

items = {}
for _ in range(2):
_item = deepcopy(item)
_item["id"] = str(uuid.uuid4())
# remove collection ID here
del _item["collection"]
items[_item["id"]] = _item

payload = {"items": items, "method": "insert"}

resp = await app_client.post(
f"/collections/{coll['id']}/bulk_items",
json=payload,
)
assert resp.status_code == 200
assert resp.text == '"Successfully added 2 items."'

for item_id in items.keys():
resp = await app_client.get(f"/collections/{coll['id']}/items/{item_id}")
assert resp.status_code == 200

# Try creating the same items again, but using upsert.
# This should succeed.
payload["method"] = "upsert"
resp = await app_client.post(
f"/collections/{coll['id']}/bulk_items",
json=payload,
)
assert resp.status_code == 200
assert resp.text == '"Successfully upserted 2 items."'


async def test_create_bulk_items_collection_mismatch(
app_client, load_test_data: Callable, load_test_collection
):
coll = load_test_collection
item = load_test_data("test_item.json")

items = {}
for _ in range(2):
_item = deepcopy(item)
_item["id"] = str(uuid.uuid4())
_item["collection"] = "wrong-collection"
items[_item["id"]] = _item

payload = {"items": items, "method": "insert"}

resp = await app_client.post(
f"/collections/{coll['id']}/bulk_items",
json=payload,
)
assert resp.status_code == 400
assert (
resp.json()["detail"]
== "Collection ID from path parameter (test-collection) does not match Collection ID from Item (wrong-collection)"
)


async def test_create_bulk_items_id_mismatch(
app_client, load_test_data: Callable, load_test_collection
):
coll = load_test_collection
item = load_test_data("test_item.json")

items = {}
for _ in range(2):
_item = deepcopy(item)
_item["id"] = str(uuid.uuid4())
_item["collection"] = "wrong-collection"
items[_item["id"] + "wrong"] = _item

payload = {"items": items, "method": "insert"}

resp = await app_client.post(
f"/collections/{coll['id']}/bulk_items",
json=payload,
)
assert resp.status_code == 400
assert (
resp.json()["detail"]
== "Collection ID from path parameter (test-collection) does not match Collection ID from Item (wrong-collection)"
)


# TODO since right now puts implement upsert
# test_create_collection_already_exists
# test create_item_already_exists
Expand Down

0 comments on commit cb08b0f

Please sign in to comment.