boto / boto3

AWS SDK for Python
https://aws.amazon.com/sdk-for-python/
Apache License 2.0
8.92k stars 1.85k forks source link

Dynamodb transact_write_items throws a exception validating ConditionExpression #4053

Open LiamCato opened 5 months ago

LiamCato commented 5 months ago

Describe the bug

When using an object supplied from from boto3.dynamodb.conditions import Attr rather than string expression transact_write_items raises an exception:

Unknown parameter in input: "ExpressionAttributeNames", must be one of: TransactItems, ReturnConsumedCapacity, ReturnItemCollectionMetrics, ClientRequestToken

When a ConditionExpression is supplied for a put.

Expected Behavior

I expect the transact_write_items call to function the same where the string representation of a ConditionExpression is supplied or the python object is supplied.

Current Behavior

The string expression works as expected, whereas when using the Attr object, the inject_condition_expressions function unpacks the api_params into:

{'TransactItems': [{'Put': {'TableName': 'my-table', 'Item': {'PK': {'S': 'Test'}, 'SK': {'S': 'Test'}, 'data': {'S': 'Test'}}, 'ConditionExpression': 'attribute_not_exists(#n0)'}}], 'ExpressionAttributeNames': {'#n0': 'SK'}, 'ClientRequestToken': 'c55280d1-da03-4d4f-9096-2cb6b9b2ff97'}

Note how the ExpressionAttributeNames is now outside the item and is validated as part of the top level members which causes the "Unknown parameter in input" exception.

Reproduction Steps

Example:

Works:

from boto3 import resource
dynamodb = resource("dynamodb")
table = dynamodb.Table("mytable")
body = [{'Put': {'TableName': 'my-table-name', 'Item': {'PK': 'Test', 'SK': 'Test', 'data': 'Test'}, 'ConditionExpression': 'attribute_not_exists(SK)' }}]
table.meta.client.transact_write_items(TransactItems=body)

Same action using the Attr object does not work:

from boto3 import resource
from boto3.dynamodb.conditions import Attr
dynamodb = resource("dynamodb")
table = dynamodb.Table("mytable")
body = [{'Put': {'TableName': 'my-table-name', 'Item': {'PK': 'Test', 'SK': 'Test', 'data': 'Test'}, 'ConditionExpression': Attr("SK").not_exists()}}]
table.meta.client.transact_write_items(TransactItems=body)

Possible Solution

No response

Additional Information/Context

No response

SDK version used

1.34.65

Environment details (OS name and version, etc.)

Ubuntu 22.04, python 3.11

LiamCato commented 5 months ago

I've created an example pull request that would fix this. I am not attached to it, it's just a suggestion and I'm happy to throw it away it the maintainers don't like it.

I do wonder however why all the expressions are not unpacked during the recursion like in my change, rather than putting just one of the transforms attributes there and then storing placeholders for later?

tim-finnigan commented 3 months ago

Thanks for reaching out and your patience here. I'm not fully clear on the issue here and think we need some more information.

Both code snippets you provided fail with the error: AttributeError: 'dynamodb.Table' object has no attribute 'transact_write_items'. Here is the documentation for that client method: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/transact_write_items.html

Can you provide new code snippets highlighting the issue and what you're trying to do? Adding debug logs using boto3.set_stream_logger('') (with sensitive info redacted) may also give more insight here.

LiamCato commented 3 months ago

@tim-finnigan

Ooops, sorry about that, I foolishly used a resource instead of a client in the example. I've updated the example to use the client and tested the snippet to check it gives the error I've reported.

LiamCato commented 3 months ago

Hopefully my example change and test in the linked PR should give you insight into where in the code the issue is.

https://github.com/boto/boto3/pull/4054/files

tim-finnigan commented 2 months ago

Thanks for following up and your patience here. For readability I'll share your snippets again here but formatted and in Python markdown syntax:

  1. (working)
    
    from boto3 import resource

dynamodb = resource("dynamodb") table = dynamodb.Table("mytable")

body = [ { 'Put': { 'TableName': 'my-table-name', 'Item': { 'PK': 'Test', 'SK': 'Test', 'data': 'Test' }, 'ConditionExpression': 'attribute_not_exists(SK)' } } ]

table.meta.client.transact_write_items(TransactItems=body)

2. (not working)
```python
from boto3 import resource
from boto3.dynamodb.conditions import Attr

dynamodb = resource("dynamodb")
table = dynamodb.Table("mytable")

body = [
    {
        'Put': {
            'TableName': 'my-table-name',
            'Item': {
                'PK': 'Test',
                'SK': 'Test',
                'data': 'Test'
            },
            'ConditionExpression': Attr("SK").not_exists()
        }
    }
]

table.meta.client.transact_write_items(TransactItems=body)

So the key difference to highlight is that 'ConditionExpression': 'attribute_not_exists(SK)' works but 'ConditionExpression': Attr("SK").not_exists() does not, because a string is expected for ConditionExpression, but you're using boto3.dynamodb.conditions.Attr.

It looks like https://github.com/boto/boto3/issues/3259 overlaps with the request here, can you confirm? I think the changes in your PR would be breaking but maybe there is another approach that could work for converting that to a string.

github-actions[bot] commented 1 month ago

Greetings! It looks like this issue hasn’t been active in longer than five days. We encourage you to check if this is still an issue in the latest release. In the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or upvote with a reaction on the initial post to prevent automatic closure. If the issue is already closed, please feel free to open a new one.

LiamCato commented 1 month ago

Hi Tim, yes I think that issue https://github.com/boto/boto3/issues/3259 overlaps with this. If there was a string representation of the Attr object then this would likely not be an issue.

tim-finnigan commented 1 month ago

Thanks for following up and confirming. We may want to consolidate these issues and continue tracking in https://github.com/boto/boto3/issues/3259, but I'll plan to bring these issues up for a future review with the team.