diff --git a/pynamodb/connection/base.py b/pynamodb/connection/base.py index 3e20255d..00d4d5c6 100644 --- a/pynamodb/connection/base.py +++ b/pynamodb/connection/base.py @@ -357,6 +357,7 @@ def _make_api_call(self, operation_name: str, operation_kwargs: Dict) -> Dict: CancellationReason( code=d['Code'], message=d.get('Message'), + item=d.get('Item'), ) if d['Code'] != 'None' else None ) for d in cancellation_reasons diff --git a/pynamodb/exceptions.py b/pynamodb/exceptions.py index 1c78c69f..0f85b1c7 100644 --- a/pynamodb/exceptions.py +++ b/pynamodb/exceptions.py @@ -134,6 +134,7 @@ class CancellationReason: """ code: str message: Optional[str] = None + item: Optional[Dict[str, Any]] = None class TransactWriteError(PynamoDBException): diff --git a/tests/integration/test_transaction_integration.py b/tests/integration/test_transaction_integration.py index 48ef041c..48e44641 100644 --- a/tests/integration/test_transaction_integration.py +++ b/tests/integration/test_transaction_integration.py @@ -5,6 +5,7 @@ import pytest from pynamodb.connection import Connection +from pynamodb.constants import ALL_OLD from pynamodb.exceptions import CancellationReason from pynamodb.exceptions import DoesNotExist, TransactWriteError, InvalidStateError @@ -168,6 +169,24 @@ def test_transact_write__error__transaction_cancelled__condition_check_failure(c assert BankStatement.Meta.table_name in exc_info.value.cause.MSG_TEMPLATE +@pytest.mark.ddblocal +def test_transact_write__error__transaction_cancelled__condition_check_failure__return_all_old(connection): + # create a users and a bank statements for them + User(1).save() + + # attempt to do this as a transaction with the condition that they don't already exist + with pytest.raises(TransactWriteError) as exc_info: + with TransactWrite(connection=connection) as transaction: + transaction.save(User(1), condition=(User.user_id.does_not_exist()), return_values=ALL_OLD) + assert exc_info.value.cause_response_code == TRANSACTION_CANCELLED + assert 'ConditionalCheckFailed' in exc_info.value.cause_response_message + assert exc_info.value.cancellation_reasons == [ + CancellationReason(code='ConditionalCheckFailed', message='The conditional request failed', item=User(1).to_dynamodb_dict()), + ] + assert isinstance(exc_info.value.cause, botocore.exceptions.ClientError) + assert User.Meta.table_name in exc_info.value.cause.MSG_TEMPLATE + + @pytest.mark.ddblocal def test_transact_write__error__transaction_cancelled__partial_failure(connection): User(2).delete()