DynamoDB:batch_put_item() now has validatior for empty keys (#5439)
This commit is contained in:
parent
53efd628c4
commit
f23b44e256
@ -1381,9 +1381,7 @@ class DynamoDBBackend(BaseBackend):
|
|||||||
for key in keys:
|
for key in keys:
|
||||||
if key in table.hash_key_names:
|
if key in table.hash_key_names:
|
||||||
return key, None
|
return key, None
|
||||||
# for potential_hash, potential_range in zip(table.hash_key_names, table.range_key_names):
|
|
||||||
# if set([potential_hash, potential_range]) == set(keys):
|
|
||||||
# return potential_hash, potential_range
|
|
||||||
potential_hash, potential_range = None, None
|
potential_hash, potential_range = None, None
|
||||||
for key in set(keys):
|
for key in set(keys):
|
||||||
if key in table.hash_key_names:
|
if key in table.hash_key_names:
|
||||||
|
@ -68,7 +68,10 @@ def include_consumed_capacity(val=1.0):
|
|||||||
return _inner
|
return _inner
|
||||||
|
|
||||||
|
|
||||||
def put_has_empty_keys(field_updates, table):
|
def get_empty_keys_on_put(field_updates, table):
|
||||||
|
"""
|
||||||
|
Return the first key-name that has an empty value. None if all keys are filled
|
||||||
|
"""
|
||||||
if table:
|
if table:
|
||||||
key_names = table.attribute_keys
|
key_names = table.attribute_keys
|
||||||
|
|
||||||
@ -78,7 +81,9 @@ def put_has_empty_keys(field_updates, table):
|
|||||||
for (key, val) in field_updates.items()
|
for (key, val) in field_updates.items()
|
||||||
if next(iter(val.keys())) in ["S", "B"] and next(iter(val.values())) == ""
|
if next(iter(val.keys())) in ["S", "B"] and next(iter(val.values())) == ""
|
||||||
]
|
]
|
||||||
return any([keyname in empty_str_fields for keyname in key_names])
|
return next(
|
||||||
|
(keyname for keyname in key_names if keyname in empty_str_fields), None
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -371,9 +376,10 @@ class DynamoHandler(BaseResponse):
|
|||||||
if return_values not in ("ALL_OLD", "NONE"):
|
if return_values not in ("ALL_OLD", "NONE"):
|
||||||
raise MockValidationException("Return values set to invalid value")
|
raise MockValidationException("Return values set to invalid value")
|
||||||
|
|
||||||
if put_has_empty_keys(item, self.dynamodb_backend.get_table(name)):
|
empty_key = get_empty_keys_on_put(item, self.dynamodb_backend.get_table(name))
|
||||||
|
if empty_key:
|
||||||
raise MockValidationException(
|
raise MockValidationException(
|
||||||
"One or more parameter values were invalid: An AttributeValue may not contain an empty string"
|
f"One or more parameter values were invalid: An AttributeValue may not contain an empty string. Key: {empty_key}"
|
||||||
)
|
)
|
||||||
if put_has_empty_attrs(item, self.dynamodb_backend.get_table(name)):
|
if put_has_empty_attrs(item, self.dynamodb_backend.get_table(name)):
|
||||||
raise MockValidationException(
|
raise MockValidationException(
|
||||||
@ -421,17 +427,29 @@ class DynamoHandler(BaseResponse):
|
|||||||
|
|
||||||
def batch_write_item(self):
|
def batch_write_item(self):
|
||||||
table_batches = self.body["RequestItems"]
|
table_batches = self.body["RequestItems"]
|
||||||
|
put_requests = []
|
||||||
|
delete_requests = []
|
||||||
for table_name, table_requests in table_batches.items():
|
for table_name, table_requests in table_batches.items():
|
||||||
|
table = self.dynamodb_backend.get_table(table_name)
|
||||||
for table_request in table_requests:
|
for table_request in table_requests:
|
||||||
request_type = list(table_request.keys())[0]
|
request_type = list(table_request.keys())[0]
|
||||||
request = list(table_request.values())[0]
|
request = list(table_request.values())[0]
|
||||||
if request_type == "PutRequest":
|
if request_type == "PutRequest":
|
||||||
item = request["Item"]
|
item = request["Item"]
|
||||||
self.dynamodb_backend.put_item(table_name, item)
|
empty_key = get_empty_keys_on_put(item, table)
|
||||||
|
if empty_key:
|
||||||
|
raise MockValidationException(
|
||||||
|
f"One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: {empty_key}"
|
||||||
|
)
|
||||||
|
put_requests.append((table_name, item))
|
||||||
elif request_type == "DeleteRequest":
|
elif request_type == "DeleteRequest":
|
||||||
keys = request["Key"]
|
keys = request["Key"]
|
||||||
self.dynamodb_backend.delete_item(table_name, keys)
|
delete_requests.append((table_name, keys))
|
||||||
|
|
||||||
|
for (table_name, item) in put_requests:
|
||||||
|
self.dynamodb_backend.put_item(table_name, item)
|
||||||
|
for (table_name, keys) in delete_requests:
|
||||||
|
self.dynamodb_backend.delete_item(table_name, keys)
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
"ConsumedCapacity": [
|
"ConsumedCapacity": [
|
||||||
|
@ -625,3 +625,45 @@ def test_update_expression_with_trailing_comma():
|
|||||||
err["Message"].should.equal(
|
err["Message"].should.equal(
|
||||||
'Invalid UpdateExpression: Syntax error; token: "<EOF>", near: ","'
|
'Invalid UpdateExpression: Syntax error; token: "<EOF>", near: ","'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_dynamodb
|
||||||
|
def test_batch_put_item_with_empty_value():
|
||||||
|
ddb = boto3.resource("dynamodb", region_name="us-east-1")
|
||||||
|
ddb.create_table(
|
||||||
|
AttributeDefinitions=[
|
||||||
|
{"AttributeName": "pk", "AttributeType": "S"},
|
||||||
|
{"AttributeName": "sk", "AttributeType": "S"},
|
||||||
|
],
|
||||||
|
TableName="test-table",
|
||||||
|
KeySchema=[
|
||||||
|
{"AttributeName": "pk", "KeyType": "HASH"},
|
||||||
|
{"AttributeName": "sk", "KeyType": "SORT"},
|
||||||
|
],
|
||||||
|
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
||||||
|
)
|
||||||
|
table = ddb.Table("test-table")
|
||||||
|
|
||||||
|
# Empty Partition Key throws an error
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
with table.batch_writer() as batch:
|
||||||
|
batch.put_item(Item={"pk": "", "sk": "sth"})
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: pk"
|
||||||
|
)
|
||||||
|
err["Code"].should.equal("ValidationException")
|
||||||
|
|
||||||
|
# Empty SortKey throws an error
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
with table.batch_writer() as batch:
|
||||||
|
batch.put_item(Item={"pk": "sth", "sk": ""})
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: sk"
|
||||||
|
)
|
||||||
|
err["Code"].should.equal("ValidationException")
|
||||||
|
|
||||||
|
# Empty regular parameter workst just fine though
|
||||||
|
with table.batch_writer() as batch:
|
||||||
|
batch.put_item(Item={"pk": "sth", "sk": "else", "par": ""})
|
||||||
|
@ -200,7 +200,7 @@ def test_item_add_empty_string_hash_key_exception():
|
|||||||
|
|
||||||
ex.value.response["Error"]["Code"].should.equal("ValidationException")
|
ex.value.response["Error"]["Code"].should.equal("ValidationException")
|
||||||
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
||||||
ex.value.response["Error"]["Message"].should.equal(
|
ex.value.response["Error"]["Message"].should.match(
|
||||||
"One or more parameter values were invalid: An AttributeValue may not contain an empty string"
|
"One or more parameter values were invalid: An AttributeValue may not contain an empty string"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ def test_item_add_empty_string_range_key_exception():
|
|||||||
|
|
||||||
ex.value.response["Error"]["Code"].should.equal("ValidationException")
|
ex.value.response["Error"]["Code"].should.equal("ValidationException")
|
||||||
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
|
||||||
ex.value.response["Error"]["Message"].should.equal(
|
ex.value.response["Error"]["Message"].should.match(
|
||||||
"One or more parameter values were invalid: An AttributeValue may not contain an empty string"
|
"One or more parameter values were invalid: An AttributeValue may not contain an empty string"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user