From 476fd895b08c2eb5e861232f90d11dc880267e87 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Mon, 11 Oct 2021 22:16:46 +0000 Subject: [PATCH] DynamoDB - Validate empty ExpressionAttributeNames on get_item() (#4393) --- moto/dynamodb2/responses.py | 13 ++- .../test_dynamodb_exceptions.py | 89 +++++++++++++++---- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index c182e2307..884a7ea71 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -410,8 +410,19 @@ class DynamoHandler(BaseResponse): ) key = self.body["Key"] projection_expression = self.body.get("ProjectionExpression") - expression_attribute_names = self.body.get("ExpressionAttributeNames", {}) + expression_attribute_names = self.body.get("ExpressionAttributeNames") + if expression_attribute_names == {}: + if projection_expression is None: + er = "ValidationException" + return self.error( + er, + "ExpressionAttributeNames can only be specified when using expressions", + ) + else: + er = "ValidationException" + return self.error(er, "ExpressionAttributeNames must not be empty") + expression_attribute_names = expression_attribute_names or {} projection_expression = self._adjust_projection_expression( projection_expression, expression_attribute_names ) diff --git a/tests/test_dynamodb2/test_dynamodb_exceptions.py b/tests/test_dynamodb2/test_dynamodb_exceptions.py index 0b707e207..c2f8a842d 100644 --- a/tests/test_dynamodb2/test_dynamodb_exceptions.py +++ b/tests/test_dynamodb2/test_dynamodb_exceptions.py @@ -6,27 +6,28 @@ from botocore.exceptions import ClientError from moto import mock_dynamodb2 +table_schema = { + "KeySchema": [{"AttributeName": "partitionKey", "KeyType": "HASH"}], + "GlobalSecondaryIndexes": [ + { + "IndexName": "GSI-K1", + "KeySchema": [ + {"AttributeName": "gsiK1PartitionKey", "KeyType": "HASH"}, + {"AttributeName": "gsiK1SortKey", "KeyType": "RANGE"}, + ], + "Projection": {"ProjectionType": "KEYS_ONLY",}, + } + ], + "AttributeDefinitions": [ + {"AttributeName": "partitionKey", "AttributeType": "S"}, + {"AttributeName": "gsiK1PartitionKey", "AttributeType": "S"}, + {"AttributeName": "gsiK1SortKey", "AttributeType": "S"}, + ], +} + + @mock_dynamodb2 def test_query_gsi_with_wrong_key_attribute_names_throws_exception(): - table_schema = { - "KeySchema": [{"AttributeName": "partitionKey", "KeyType": "HASH"}], - "GlobalSecondaryIndexes": [ - { - "IndexName": "GSI-K1", - "KeySchema": [ - {"AttributeName": "gsiK1PartitionKey", "KeyType": "HASH"}, - {"AttributeName": "gsiK1SortKey", "KeyType": "RANGE"}, - ], - "Projection": {"ProjectionType": "KEYS_ONLY",}, - } - ], - "AttributeDefinitions": [ - {"AttributeName": "partitionKey", "AttributeType": "S"}, - {"AttributeName": "gsiK1PartitionKey", "AttributeType": "S"}, - {"AttributeName": "gsiK1SortKey", "AttributeType": "S"}, - ], - } - item = { "partitionKey": "pk-1", "gsiK1PartitionKey": "gsi-pk", @@ -96,3 +97,53 @@ def test_query_gsi_with_wrong_key_attribute_names_throws_exception(): err["Message"].should.equal( "Query condition missed key schema element: gsiK1SortKey" ) + + +@mock_dynamodb2 +def test_empty_expressionattributenames(): + ddb = boto3.resource("dynamodb", region_name="us-east-1") + ddb.create_table( + TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema + ) + table = ddb.Table("test-table") + with pytest.raises(ClientError) as exc: + table.get_item( + Key={"id": "my_id"}, ExpressionAttributeNames={}, + ) + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal( + "ExpressionAttributeNames can only be specified when using expressions" + ) + + +@mock_dynamodb2 +def test_empty_expressionattributenames_with_empty_projection(): + ddb = boto3.resource("dynamodb", region_name="us-east-1") + ddb.create_table( + TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema + ) + table = ddb.Table("test-table") + with pytest.raises(ClientError) as exc: + table.get_item( + Key={"id": "my_id"}, ProjectionExpression="", ExpressionAttributeNames={}, + ) + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal("ExpressionAttributeNames must not be empty") + + +@mock_dynamodb2 +def test_empty_expressionattributenames_with_projection(): + ddb = boto3.resource("dynamodb", region_name="us-east-1") + ddb.create_table( + TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema + ) + table = ddb.Table("test-table") + with pytest.raises(ClientError) as exc: + table.get_item( + Key={"id": "my_id"}, ProjectionExpression="id", ExpressionAttributeNames={}, + ) + err = exc.value.response["Error"] + err["Code"].should.equal("ValidationException") + err["Message"].should.equal("ExpressionAttributeNames must not be empty")