Skip to content

Commit 79c0cee

Browse files
jonhealy1robintwgeospatial-jeff
authored
Update item transactions in SQLAlchemy (#303)
* query by item and collection id * add get item test * add parameters * run pre-commit * Alter items table to have primary key on both id and collection_id * update update_item route * add delete_item changes * run pre-commit * run pre-commit * clean up * fix query in update/delete items * run pre commit * create duplicate item ids, different collections test * create duplicate item ids, delete item test * create duplicate item ids, update item test * clarify tests * create duplicate item, different collections test in postgres * remove app client 2 in conftest Co-authored-by: Robin Wilson <[email protected]> Co-authored-by: Jeff Albrecht <[email protected]>
1 parent 76e8d64 commit 79c0cee

File tree

3 files changed

+185
-4
lines changed

3 files changed

+185
-4
lines changed

stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/transactions.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@ def update_item(self, model: stac_types.Item, **kwargs) -> stac_types.Item:
5858
query = session.query(self.item_table).filter(
5959
self.item_table.id == model["id"]
6060
)
61+
query = query.filter(self.item_table.collection_id == model["collection"])
6162
if not query.scalar():
62-
raise NotFoundError(f"Item {model['id']} not found")
63+
raise NotFoundError(
64+
f"Item {model['id']} in collection {model['collection']}"
65+
)
6366
# SQLAlchemy orm updates don't seem to like geoalchemy types
6467
db_model = self.item_serializer.stac_to_db(model)
6568
query.update(self.item_serializer.row_to_dict(db_model))
@@ -91,10 +94,15 @@ def delete_item(
9194
"""Delete item."""
9295
base_url = str(kwargs["request"].base_url)
9396
with self.session.writer.context_session() as session:
94-
query = session.query(self.item_table).filter(self.item_table.id == item_id)
97+
query = session.query(self.item_table).filter(
98+
self.item_table.collection_id == collection_id
99+
)
100+
query = query.filter(self.item_table.id == item_id)
95101
data = query.first()
96102
if not data:
97-
raise NotFoundError(f"Item {item_id} not found")
103+
raise NotFoundError(
104+
f"Item {item_id} not found in collection {collection_id}"
105+
)
98106
query.delete()
99107
return self.item_serializer.db_to_stac(data, base_url=base_url)
100108

stac_fastapi/sqlalchemy/tests/clients/test_postgres.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,44 @@ def test_create_item_already_exists(
157157
postgres_transactions.create_item(item, request=MockStarletteRequest)
158158

159159

160+
def test_create_duplicate_item_different_collections(
161+
postgres_core: CoreCrudClient,
162+
postgres_transactions: TransactionsClient,
163+
load_test_data: Callable,
164+
):
165+
# create test-collection
166+
coll = load_test_data("test_collection.json")
167+
postgres_transactions.create_collection(coll, request=MockStarletteRequest)
168+
169+
# create test-collection-2
170+
coll["id"] = "test-collection-2"
171+
postgres_transactions.create_collection(coll, request=MockStarletteRequest)
172+
173+
# add item to test-collection
174+
item = load_test_data("test_item.json")
175+
postgres_transactions.create_item(item, request=MockStarletteRequest)
176+
177+
# get item from test-collection
178+
resp = postgres_core.get_item(
179+
item["id"], item["collection"], request=MockStarletteRequest
180+
)
181+
assert Item(**item).dict(
182+
exclude={"links": ..., "properties": {"created", "updated"}}
183+
) == Item(**resp).dict(exclude={"links": ..., "properties": {"created", "updated"}})
184+
185+
# add item to test-collection-2
186+
item["collection"] = "test-collection-2"
187+
postgres_transactions.create_item(item, request=MockStarletteRequest)
188+
189+
# get item with same id from test-collection-2
190+
resp = postgres_core.get_item(
191+
item["id"], item["collection"], request=MockStarletteRequest
192+
)
193+
assert Item(**item).dict(
194+
exclude={"links": ..., "properties": {"created", "updated"}}
195+
) == Item(**resp).dict(exclude={"links": ..., "properties": {"created", "updated"}})
196+
197+
160198
def test_update_item(
161199
postgres_core: CoreCrudClient,
162200
postgres_transactions: TransactionsClient,

stac_fastapi/sqlalchemy/tests/resources/test_item.py

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,141 @@ def test_create_item_conflict(app_client, load_test_data):
4444
assert resp.status_code == 409
4545

4646

47+
def test_create_item_duplicate(app_client, load_test_data):
48+
"""Test creation of an item id which already exists but in a different collection(transactions extension)"""
49+
50+
# add test_item to test-collection
51+
test_item = load_test_data("test_item.json")
52+
resp = app_client.post(
53+
f"/collections/{test_item['collection']}/items", json=test_item
54+
)
55+
assert resp.status_code == 200
56+
57+
# add test_item to test-collection again, resource already exists
58+
test_item = load_test_data("test_item.json")
59+
resp = app_client.post(
60+
f"/collections/{test_item['collection']}/items", json=test_item
61+
)
62+
assert resp.status_code == 409
63+
64+
# create "test-collection-2"
65+
collection_2 = load_test_data("test_collection.json")
66+
collection_2["id"] = "test-collection-2"
67+
resp = app_client.post("/collections", json=collection_2)
68+
assert resp.status_code == 200
69+
70+
# add test_item to test-collection-2, posts successfully
71+
test_item["collection"] = "test-collection-2"
72+
resp = app_client.post(
73+
f"/collections/{test_item['collection']}/items", json=test_item
74+
)
75+
assert resp.status_code == 200
76+
77+
78+
def test_delete_item_duplicate(app_client, load_test_data):
79+
"""Test creation of an item id which already exists but in a different collection(transactions extension)"""
80+
81+
# add test_item to test-collection
82+
test_item = load_test_data("test_item.json")
83+
resp = app_client.post(
84+
f"/collections/{test_item['collection']}/items", json=test_item
85+
)
86+
assert resp.status_code == 200
87+
88+
# create "test-collection-2"
89+
collection_2 = load_test_data("test_collection.json")
90+
collection_2["id"] = "test-collection-2"
91+
resp = app_client.post("/collections", json=collection_2)
92+
assert resp.status_code == 200
93+
94+
# add test_item to test-collection-2
95+
test_item["collection"] = "test-collection-2"
96+
resp = app_client.post(
97+
f"/collections/{test_item['collection']}/items", json=test_item
98+
)
99+
assert resp.status_code == 200
100+
101+
# delete test_item from test-collection
102+
test_item["collection"] = "test-collection"
103+
resp = app_client.delete(
104+
f"/collections/{test_item['collection']}/items/{test_item['id']}"
105+
)
106+
assert resp.status_code == 200
107+
108+
# test-item in test-collection has already been deleted
109+
resp = app_client.delete(
110+
f"/collections/{test_item['collection']}/items/{test_item['id']}"
111+
)
112+
assert resp.status_code == 404
113+
114+
# test-item in test-collection-2 still exists, was not deleted
115+
test_item["collection"] = "test-collection-2"
116+
resp = app_client.post(
117+
f"/collections/{test_item['collection']}/items", json=test_item
118+
)
119+
assert resp.status_code == 409
120+
121+
122+
def test_update_item_duplicate(app_client, load_test_data):
123+
"""Test creation of an item id which already exists but in a different collection(transactions extension)"""
124+
125+
# add test_item to test-collection
126+
test_item = load_test_data("test_item.json")
127+
resp = app_client.post(
128+
f"/collections/{test_item['collection']}/items", json=test_item
129+
)
130+
assert resp.status_code == 200
131+
132+
# create "test-collection-2"
133+
collection_2 = load_test_data("test_collection.json")
134+
collection_2["id"] = "test-collection-2"
135+
resp = app_client.post("/collections", json=collection_2)
136+
assert resp.status_code == 200
137+
138+
# add test_item to test-collection-2
139+
test_item["collection"] = "test-collection-2"
140+
resp = app_client.post(
141+
f"/collections/{test_item['collection']}/items", json=test_item
142+
)
143+
assert resp.status_code == 200
144+
145+
# update gsd in test_item, test-collection-2
146+
test_item["properties"]["gsd"] = 16
147+
resp = app_client.put(
148+
f"/collections/{test_item['collection']}/items", json=test_item
149+
)
150+
assert resp.status_code == 200
151+
updated_item = resp.json()
152+
assert updated_item["properties"]["gsd"] == 16
153+
154+
# update gsd in test_item, test-collection
155+
test_item["collection"] = "test-collection"
156+
test_item["properties"]["gsd"] = 17
157+
resp = app_client.put(
158+
f"/collections/{test_item['collection']}/items", json=test_item
159+
)
160+
assert resp.status_code == 200
161+
updated_item = resp.json()
162+
assert updated_item["properties"]["gsd"] == 17
163+
164+
# test_item in test-collection, updated gsd = 17
165+
resp = app_client.get(
166+
f"/collections/{test_item['collection']}/items/{test_item['id']}"
167+
)
168+
assert resp.status_code == 200
169+
item = resp.json()
170+
assert item["properties"]["gsd"] == 17
171+
172+
# test_item in test-collection-2, updated gsd = 16
173+
test_item["collection"] = "test-collection-2"
174+
resp = app_client.get(
175+
f"/collections/{test_item['collection']}/items/{test_item['id']}"
176+
)
177+
assert resp.status_code == 200
178+
item = resp.json()
179+
assert item["properties"]["gsd"] == 16
180+
181+
47182
def test_delete_missing_item(app_client, load_test_data):
48183
"""Test deletion of an item which does not exist (transactions extension)"""
49184
test_item = load_test_data("test_item.json")
@@ -102,7 +237,7 @@ def test_update_item_missing_collection(app_client, load_test_data):
102237
resp = app_client.put(
103238
f"/collections/{test_item['collection']}/items", json=test_item
104239
)
105-
assert resp.status_code == 422
240+
assert resp.status_code == 404
106241

107242

108243
def test_update_item_geometry(app_client, load_test_data):

0 commit comments

Comments
 (0)