Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DyanmoDB: empty update expression in update_item raises the wrong exception #7988

Closed
felixscherz opened this issue Aug 16, 2024 · 0 comments · Fixed by #7989
Closed

DyanmoDB: empty update expression in update_item raises the wrong exception #7988

felixscherz opened this issue Aug 16, 2024 · 0 comments · Fixed by #7989

Comments

@felixscherz
Copy link
Contributor

Hi, thanks for maintaining this package!

I was working with DynamoDB and found a bug that occurs when the update expression given to Table.update_item is an empty string "".

How to reproduce

import boto3
import moto

with moto.mock_aws():
    dynamodb = boto3.client("dynamodb", "us-east-1")

    dynamodb.create_table(
        TableName="foo",
        KeySchema=[
            {"AttributeName": "pk", "KeyType": "HASH"},
        ],
        AttributeDefinitions=[
            {"AttributeName": "pk", "AttributeType": "S"},
        ],
        BillingMode="PAY_PER_REQUEST"
    )

    dynamodb.put_item(TableName="foo", Item={"pk": {"S": "foo"}})
    dynamodb.update_item(
        TableName="foo",
        Key={"pk": {"S": "foo"}},
        UpdateExpression="",
    )

raises an AttributeError:

Traceback (most recent call last):
  File "/private/tmp/moto-dynamodb/t.py", line 19, in <module>
    dynamodb.update_item(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/client.py", line 565, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/client.py", line 999, in _make_api_call
    http, parsed_response = self._make_request(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/client.py", line 1023, in _make_request
    return self._endpoint.make_request(operation_model, request_dict)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/endpoint.py", line 119, in make_request
    return self._send_request(request_dict, operation_model)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/endpoint.py", line 200, in _send_request
    while self._needs_retry(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/endpoint.py", line 352, in _needs_retry
    responses = self._event_emitter.emit(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 412, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 256, in emit
    return self._emit(event_name, kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 239, in _emit
    response = handler(**kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 207, in __call__
    if self._checker(**checker_kwargs):
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 284, in __call__
    should_retry = self._should_retry(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 307, in _should_retry
    return self._checker(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 363, in __call__
    checker_response = checker(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 247, in __call__
    return self._check_caught_exception(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/retryhandler.py", line 416, in _check_caught_exception
    raise caught_exception
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/endpoint.py", line 276, in _do_get_response
    responses = self._event_emitter.emit(event_name, request=request)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 412, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 256, in emit
    return self._emit(event_name, kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/hooks.py", line 239, in _emit
    response = handler(**kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/core/botocore_stubber.py", line 37, in __call__
    response = self.process_request(request)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/core/botocore_stubber.py", line 84, in process_request
    status, headers, body = method_to_execute(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/core/responses.py", line 290, in dispatch
    return cls()._dispatch(*args, **kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/core/responses.py", line 501, in _dispatch
    return self.call_action()
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/utilities/aws_headers.py", line 44, in _wrapper
    response = f(*args, **kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/dynamodb/responses.py", line 196, in call_action
    return super().call_action()
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/core/responses.py", line 587, in call_action
    response = method()
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/dynamodb/responses.py", line 928, in update_item
    item = self.dynamodb_backend.update_item(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/dynamodb/models/__init__.py", line 560, in update_item
    item.update_with_attribute_updates(attribute_updates)  # type: ignore
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/moto/dynamodb/models/dynamo_type.py", line 368, in update_with_attribute_updates
    for attribute_name, update_action in attribute_updates.items():
AttributeError: 'NoneType' object has no attribute 'items'

Expected outcome

When running the request against AWS directly a ClientError is raised:

Traceback (most recent call last):
  File "/private/tmp/moto-dynamodb/t.py", line 35, in <module>
    dynamodb.update_item(
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/client.py", line 565, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/private/tmp/moto-dynamodb/venv/lib/python3.10/site-packages/botocore/client.py", line 1017, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression: The expression can not be empty;

I had a look at the code already and I will submit a pull request with a possible fix shortly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants