DynamoDB: put_item(): Improve type validation (#5654)
This commit is contained in:
		
							parent
							
								
									96b2eff1bc
								
							
						
					
					
						commit
						37845792d3
					
				| @ -332,3 +332,8 @@ class TransactWriteSingleOpException(MockValidationException): | |||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         super().__init__(self.there_can_be_only_one) |         super().__init__(self.there_can_be_only_one) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SerializationException(DynamodbException): | ||||||
|  |     def __init__(self, msg): | ||||||
|  |         super().__init__(error_type="SerializationException", message=msg) | ||||||
|  | |||||||
| @ -33,6 +33,7 @@ from moto.dynamodb.exceptions import ( | |||||||
|     MockValidationException, |     MockValidationException, | ||||||
|     InvalidConversion, |     InvalidConversion, | ||||||
|     TransactWriteSingleOpException, |     TransactWriteSingleOpException, | ||||||
|  |     SerializationException, | ||||||
| ) | ) | ||||||
| from moto.dynamodb.models.utilities import bytesize | from moto.dynamodb.models.utilities import bytesize | ||||||
| from moto.dynamodb.models.dynamo_type import DynamoType | from moto.dynamodb.models.dynamo_type import DynamoType | ||||||
| @ -658,6 +659,17 @@ class Table(CloudFormationModel): | |||||||
|                 self._validate_item_types(value) |                 self._validate_item_types(value) | ||||||
|             elif type(value) == int and key == "N": |             elif type(value) == int and key == "N": | ||||||
|                 raise InvalidConversion |                 raise InvalidConversion | ||||||
|  |             if key == "S": | ||||||
|  |                 # This scenario is usually caught by boto3, but the user can disable parameter validation | ||||||
|  |                 # Which is why we need to catch it 'server-side' as well | ||||||
|  |                 if type(value) == int: | ||||||
|  |                     raise SerializationException( | ||||||
|  |                         "NUMBER_VALUE cannot be converted to String" | ||||||
|  |                     ) | ||||||
|  |                 if type(value) == dict: | ||||||
|  |                     raise SerializationException( | ||||||
|  |                         "Start of structure or map found where not expected" | ||||||
|  |                     ) | ||||||
| 
 | 
 | ||||||
|     def put_item( |     def put_item( | ||||||
|         self, |         self, | ||||||
| @ -699,9 +711,8 @@ class Table(CloudFormationModel): | |||||||
|                 actual_type=range_value.type, |                 actual_type=range_value.type, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         self._validate_key_sizes(item_attrs) |  | ||||||
| 
 |  | ||||||
|         self._validate_item_types(item_attrs) |         self._validate_item_types(item_attrs) | ||||||
|  |         self._validate_key_sizes(item_attrs) | ||||||
| 
 | 
 | ||||||
|         if expected is None: |         if expected is None: | ||||||
|             expected = {} |             expected = {} | ||||||
|  | |||||||
| @ -870,3 +870,29 @@ def test_update_primary_key(): | |||||||
|     table.get_item(Key={"pk": "testchangepk"})["Item"].should.equal( |     table.get_item(Key={"pk": "testchangepk"})["Item"].should.equal( | ||||||
|         {"pk": "testchangepk"} |         {"pk": "testchangepk"} | ||||||
|     ) |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_dynamodb | ||||||
|  | def test_put_item__string_as_integer_value(): | ||||||
|  |     if settings.TEST_SERVER_MODE: | ||||||
|  |         raise SkipTest("Unable to mock a session with Config in ServerMode") | ||||||
|  |     session = botocore.session.Session() | ||||||
|  |     config = botocore.client.Config(parameter_validation=False) | ||||||
|  |     client = session.create_client("dynamodb", region_name="us-east-1", config=config) | ||||||
|  |     client.create_table( | ||||||
|  |         TableName="without_sk", | ||||||
|  |         KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}], | ||||||
|  |         AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}], | ||||||
|  |         ProvisionedThroughput={"ReadCapacityUnits": 10, "WriteCapacityUnits": 10}, | ||||||
|  |     ) | ||||||
|  |     with pytest.raises(ClientError) as exc: | ||||||
|  |         client.put_item(TableName="without_sk", Item={"pk": {"S": 123}}) | ||||||
|  |     err = exc.value.response["Error"] | ||||||
|  |     err["Code"].should.equal("SerializationException") | ||||||
|  |     err["Message"].should.equal("NUMBER_VALUE cannot be converted to String") | ||||||
|  | 
 | ||||||
|  |     with pytest.raises(ClientError) as exc: | ||||||
|  |         client.put_item(TableName="without_sk", Item={"pk": {"S": {"S": "asdf"}}}) | ||||||
|  |     err = exc.value.response["Error"] | ||||||
|  |     err["Code"].should.equal("SerializationException") | ||||||
|  |     err["Message"].should.equal("Start of structure or map found where not expected") | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user