Skip to content

Commit 7e99e75

Browse files
committed
Merge branch 'gh-170'
2 parents b628c31 + 87ddf18 commit 7e99e75

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

src/aiodynamo/errors.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import json
2-
from typing import Any, Dict
2+
from dataclasses import dataclass
3+
from typing import Any, Dict, List, Union
4+
5+
6+
@dataclass(frozen=True)
7+
class CancellationReason:
8+
code: str
9+
message: str
310

411

512
class AIODynamoError(Exception):
@@ -110,7 +117,17 @@ class PointInTimeRecoveryUnavailable(AIODynamoError):
110117

111118

112119
class TransactionCanceled(AIODynamoError):
113-
pass
120+
cancellation_reasons: List[Union[CancellationReason, None]]
121+
122+
def __init__(self, body: Dict[str, Any]):
123+
self.body = body
124+
self.cancellation_reasons: List[CancellationReason] = [
125+
CancellationReason(reason["Code"], reason["Message"])
126+
if reason["Code"] != "None"
127+
else None
128+
for reason in self.body["CancellationReasons"]
129+
]
130+
super().__init__(body)
114131

115132

116133
class TransactionEmpty(AIODynamoError):
@@ -207,17 +224,7 @@ def exception_from_response(status: int, body: bytes) -> Exception:
207224
return ServiceUnavailable()
208225
try:
209226
data = json.loads(body)
210-
error = ERRORS[data["__type"].split("#", 1)[-1]](data)
211-
if isinstance(error, TransactionCanceled):
212-
error = extract_error_from_transaction_canceled(data)
227+
error: Exception = ERRORS[data["__type"].split("#", 1)[-1]](data)
213228
return error
214229
except Exception:
215230
return UnknownError(status, body)
216-
217-
218-
def extract_error_from_transaction_canceled(data: Dict[str, Any]) -> AIODynamoError:
219-
try:
220-
error = data["CancellationReasons"][0]
221-
return ERRORS[f"{error['Code']}Exception"](error["Message"])
222-
except Exception:
223-
return ERRORS[data["__type"].split("#", 1)[-1]](data)

tests/integration/test_client.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -624,14 +624,36 @@ async def test_transact_write_items_put(client: Client, table: TableName) -> Non
624624
await client.transact_write_items(items=puts)
625625
assert len([item async for item in client.query(table, HashKey("h", "h"))]) == 2
626626

627-
with pytest.raises(errors.ConditionalCheckFailed):
627+
with pytest.raises(errors.TransactionCanceled) as excinfo:
628628
put = Put(
629629
table=table,
630630
item={"h": "h", "r": "0", "s": "initial"},
631631
condition=F("h").does_not_exist(),
632632
)
633633
await client.transact_write_items(items=[put])
634634

635+
assert len(excinfo.value.cancellation_reasons) == 1
636+
assert excinfo.value.cancellation_reasons[0] is not None
637+
assert excinfo.value.cancellation_reasons[0].code == "ConditionalCheckFailed"
638+
639+
with pytest.raises(errors.TransactionCanceled) as excinfo:
640+
put = Put(
641+
table=table,
642+
item={"h": "h", "r": "3", "s": "initial"},
643+
condition=F("h").does_not_exist(),
644+
)
645+
put_fail = Put(
646+
table=table,
647+
item={"h": "h", "r": "0", "s": "initial"},
648+
condition=F("h").does_not_exist(),
649+
)
650+
await client.transact_write_items(items=[put, put_fail])
651+
652+
assert len(excinfo.value.cancellation_reasons) == 2
653+
assert excinfo.value.cancellation_reasons[0] is None
654+
assert excinfo.value.cancellation_reasons[1] is not None
655+
assert excinfo.value.cancellation_reasons[1].code == "ConditionalCheckFailed"
656+
635657

636658
@pytest.mark.usefixtures("supports_transactions")
637659
async def test_transact_write_items_update(client: Client, table: TableName) -> None:
@@ -647,7 +669,7 @@ async def test_transact_write_items_update(client: Client, table: TableName) ->
647669
query = await client.query_single_page(table, HashKey("h", "h"))
648670
assert query.items[0]["s"] == "changed"
649671

650-
with pytest.raises(errors.ConditionalCheckFailed):
672+
with pytest.raises(errors.TransactionCanceled) as excinfo:
651673
update = Update(
652674
table=table,
653675
key={"h": "h", "r": "1"},
@@ -656,6 +678,10 @@ async def test_transact_write_items_update(client: Client, table: TableName) ->
656678
)
657679
await client.transact_write_items(items=[update])
658680

681+
assert len(excinfo.value.cancellation_reasons) == 1
682+
assert excinfo.value.cancellation_reasons[0] is not None
683+
assert excinfo.value.cancellation_reasons[0].code == "ConditionalCheckFailed"
684+
659685

660686
@pytest.mark.usefixtures("supports_transactions")
661687
async def test_transact_write_items_delete(client: Client, table: TableName) -> None:
@@ -670,14 +696,19 @@ async def test_transact_write_items_delete(client: Client, table: TableName) ->
670696
assert len([item async for item in client.query(table, HashKey("h", "h"))]) == 0
671697

672698
await client.put_item(table=table, item={"h": "h", "r": "1", "s": "initial"})
673-
with pytest.raises(errors.ConditionalCheckFailed):
699+
700+
with pytest.raises(errors.TransactionCanceled) as excinfo:
674701
delete = Delete(
675702
table=table,
676703
key={"h": "h", "r": "1"},
677704
condition=F("s").not_equals("initial"),
678705
)
679706
await client.transact_write_items(items=[delete])
680707

708+
assert len(excinfo.value.cancellation_reasons) == 1
709+
assert excinfo.value.cancellation_reasons[0] is not None
710+
assert excinfo.value.cancellation_reasons[0].code == "ConditionalCheckFailed"
711+
681712

682713
@pytest.mark.usefixtures("supports_transactions")
683714
async def test_transact_write_items_condition_check(
@@ -687,9 +718,13 @@ async def test_transact_write_items_condition_check(
687718
condition = ConditionCheck(
688719
table=table, key={"h": "h", "r": "1"}, condition=F("s").not_equals("initial")
689720
)
690-
with pytest.raises(errors.ConditionalCheckFailed):
721+
with pytest.raises(errors.TransactionCanceled) as excinfo:
691722
await client.transact_write_items(items=[condition])
692723

724+
assert len(excinfo.value.cancellation_reasons) == 1
725+
assert excinfo.value.cancellation_reasons[0] is not None
726+
assert excinfo.value.cancellation_reasons[0].code == "ConditionalCheckFailed"
727+
693728
condition = ConditionCheck(
694729
table=table, key={"h": "h", "r": "1"}, condition=F("s").equals("initial")
695730
)

0 commit comments

Comments
 (0)