DynamoDB: Add validation for GSI attrs set to None (#5843)
This commit is contained in:
parent
e47a2fd120
commit
6e7d3ec938
@ -1422,7 +1422,7 @@ class DynamoDBBackend(BaseBackend):
|
||||
else:
|
||||
return table.schema
|
||||
|
||||
def get_table(self, table_name):
|
||||
def get_table(self, table_name) -> Table:
|
||||
if table_name not in self.tables:
|
||||
raise ResourceNotFoundException()
|
||||
return self.tables.get(table_name)
|
||||
|
@ -13,7 +13,7 @@ from .exceptions import (
|
||||
ResourceNotFoundException,
|
||||
ConditionalCheckFailed,
|
||||
)
|
||||
from moto.dynamodb.models import dynamodb_backends, dynamo_json_dump
|
||||
from moto.dynamodb.models import dynamodb_backends, dynamo_json_dump, Table
|
||||
from moto.utilities.aws_headers import amz_crc32, amzn_request_id
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ def include_consumed_capacity(val=1.0):
|
||||
return _inner
|
||||
|
||||
|
||||
def get_empty_keys_on_put(field_updates, table):
|
||||
def get_empty_keys_on_put(field_updates, table: Table):
|
||||
"""
|
||||
Return the first key-name that has an empty value. None if all keys are filled
|
||||
"""
|
||||
@ -105,6 +105,16 @@ def put_has_empty_attrs(field_updates, table):
|
||||
return False
|
||||
|
||||
|
||||
def validate_put_has_gsi_keys_set_to_none(item, table: Table) -> None:
|
||||
for gsi in table.global_indexes:
|
||||
for attr in gsi.schema:
|
||||
attr_name = attr["AttributeName"]
|
||||
if attr_name in item and item[attr_name] == {"NULL": True}:
|
||||
raise MockValidationException(
|
||||
f"One or more parameter values were invalid: Type mismatch for Index Key {attr_name} Expected: S Actual: NULL IndexName: {gsi.name}"
|
||||
)
|
||||
|
||||
|
||||
def check_projection_expression(expression):
|
||||
if expression.upper() in ReservedKeywords.get_reserved_keywords():
|
||||
raise MockValidationException(
|
||||
@ -375,15 +385,17 @@ class DynamoHandler(BaseResponse):
|
||||
if return_values not in ("ALL_OLD", "NONE"):
|
||||
raise MockValidationException("Return values set to invalid value")
|
||||
|
||||
empty_key = get_empty_keys_on_put(item, self.dynamodb_backend.get_table(name))
|
||||
table = self.dynamodb_backend.get_table(name)
|
||||
empty_key = get_empty_keys_on_put(item, table)
|
||||
if empty_key:
|
||||
raise MockValidationException(
|
||||
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, table):
|
||||
raise MockValidationException(
|
||||
"One or more parameter values were invalid: An number set may not be empty"
|
||||
)
|
||||
validate_put_has_gsi_keys_set_to_none(item, table)
|
||||
|
||||
overwrite = "Expected" not in self.body
|
||||
if not overwrite:
|
||||
|
@ -899,3 +899,43 @@ def test_put_item__string_as_integer_value():
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("SerializationException")
|
||||
err["Message"].should.equal("Start of structure or map found where not expected")
|
||||
|
||||
|
||||
@mock_dynamodb
|
||||
def test_gsi_key_cannot_be_empty():
|
||||
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
||||
hello_index = {
|
||||
"IndexName": "hello-index",
|
||||
"KeySchema": [{"AttributeName": "hello", "KeyType": "HASH"}],
|
||||
"Projection": {"ProjectionType": "ALL"},
|
||||
}
|
||||
table_name = "lilja-test"
|
||||
|
||||
# Let's create a table with [id: str, hello: str], with an index to hello
|
||||
dynamodb.create_table(
|
||||
TableName=table_name,
|
||||
KeySchema=[
|
||||
{"AttributeName": "id", "KeyType": "HASH"},
|
||||
],
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "id", "AttributeType": "S"},
|
||||
{"AttributeName": "hello", "AttributeType": "S"},
|
||||
],
|
||||
GlobalSecondaryIndexes=[hello_index],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)
|
||||
|
||||
table = dynamodb.Table(table_name)
|
||||
with pytest.raises(ClientError) as exc:
|
||||
table.put_item(
|
||||
TableName=table_name,
|
||||
Item={
|
||||
"id": "woop",
|
||||
"hello": None,
|
||||
},
|
||||
)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ValidationException")
|
||||
err["Message"].should.equal(
|
||||
"One or more parameter values were invalid: Type mismatch for Index Key hello Expected: S Actual: NULL IndexName: hello-index"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user