f4f8527955
* fix OPTIONS requests on non-existing API GW integrations * add cloudformation models for API Gateway deployments * bump version * add backdoor to return CloudWatch metrics * Updating implementation coverage * Updating implementation coverage * add cloudformation models for API Gateway deployments * Updating implementation coverage * Updating implementation coverage * Implemented get-caller-identity returning real data depending on the access key used. * bump version * minor fixes * fix Number data_type for SQS message attribute * fix handling of encoding errors * bump version * make CF stack queryable before starting to initialize its resources * bump version * fix integration_method for API GW method integrations * fix undefined status in CF FakeStack * Fix apigateway issues with terraform v0.12.21 * resource_methods -> add handle for "DELETE" method * integrations -> fix issue that "httpMethod" wasn't included in body request (this value was set as the value from refer method resource) * bump version * Fix setting http method for API gateway integrations (#6) * bump version * remove duplicate methods * add storage class to S3 Key when completing multipart upload (#7) * fix SQS performance issues; bump version * add pagination to SecretsManager list-secrets (#9) * fix default parameter groups in RDS * fix adding S3 metadata headers with names containing dots (#13) * Updating implementation coverage * Updating implementation coverage * add cloudformation models for API Gateway deployments * Updating implementation coverage * Updating implementation coverage * Implemented get-caller-identity returning real data depending on the access key used. * make CF stack queryable before starting to initialize its resources * bump version * remove duplicate methods * fix adding S3 metadata headers with names containing dots (#13) * Update amis.json to support EKS AMI mocks (#15) * fix PascalCase for boolean value in ListMultipartUploads response (#17); fix _get_multi_param to parse nested list/dict query params * determine non-zero container exit code in Batch API * support filtering by dimensions in CW get_metric_statistics * fix storing attributes for ELBv2 Route entities; API GW refactorings for TF tests * add missing fields for API GW resources * fix error messages for Route53 (TF-compat) * various fixes for IAM resources (tf-compat) * minor fixes for API GW models (tf-compat) * minor fixes for API GW responses (tf-compat) * add s3 exception for bucket notification filter rule validation * change the way RESTErrors generate the response body and content-type header * fix lint errors and disable "black" syntax enforcement * remove return type hint in RESTError.get_body * add RESTError XML template for IAM exceptions * add support for API GW minimumCompressionSize * fix casing getting PrivateDnsEnabled API GW attribute * minor fixes for error responses * fix escaping special chars for IAM role descriptions (tf-compat) * minor fixes and tagging support for API GW and ELB v2 (tf-compat) * Merge branch 'master' into localstack * add "AlarmRule" attribute to enable support for composite CloudWatch metrics * fix recursive parsing of complex/nested query params * bump version * add API to delete S3 website configurations (#18) * use dict copy to allow parallelism and avoid concurrent modification exceptions in S3 * fix precondition check for etags in S3 (#19) * minor fix for user filtering in Cognito * fix API Gateway error response; avoid returning empty response templates (tf-compat) * support tags and tracingEnabled attribute for API GW stages * fix boolean value in S3 encryption response (#20) * fix connection arn structure * fix api destination arn structure * black format * release 2.0.3.37 * fix s3 exception tests see botocore/parsers.py:1002 where RequestId is removed from parsed * remove python 2 from build action * add test failure annotations in build action * fix events test arn comparisons * fix s3 encryption response test * return default value "0" if EC2 availableIpAddressCount is empty * fix extracting SecurityGroupIds for EC2 VPC endpoints * support deleting/updating API Gateway DomainNames * fix(events): Return empty string instead of null when no pattern is specified in EventPattern (tf-compat) (#22) * fix logic and revert CF changes to get tests running again (#21) * add support for EC2 customer gateway API (#25) * add support for EC2 Transit Gateway APIs (#24) * feat(logs): add `kmsKeyId` into `LogGroup` entity (#23) * minor change in ELBv2 logic to fix tests * feat(events): add APIs to describe and delete CloudWatch Events connections (#26) * add support for EC2 transit gateway route tables (#27) * pass transit gateway route table ID in Describe API, minor refactoring (#29) * add support for EC2 Transit Gateway Routes (#28) * fix region on ACM certificate import (#31) * add support for EC2 transit gateway attachments (#30) * add support for EC2 Transit Gateway VPN attachments (#32) * fix account ID for logs API * add support for DeleteOrganization API * feat(events): store raw filter representation for CloudWatch events patterns (tf-compat) (#36) * feat(events): add support to describe/update/delete CloudWatch API destinations (#35) * add Cognito UpdateIdentityPool, CW Logs PutResourcePolicy * feat(events): add support for tags in EventBus API (#38) * fix parameter validation for Batch compute environments (tf-compat) * revert merge conflicts in IMPLEMENTATION_COVERAGE.md * format code using black * restore original README; re-enable and fix CloudFormation tests * restore tests and old logic for CF stack parameters from SSM * parameterize RequestId/RequestID in response messages and revert related test changes * undo LocalStack-specific adaptations * minor fix * Update CodeCov config to reflect removal of Py2 * undo change related to CW metric filtering; add additional test for CW metric statistics with dimensions * Terraform - Extend whitelist of running tests Co-authored-by: acsbendi <acsbendi28@gmail.com> Co-authored-by: Phan Duong <duongpv@outlook.com> Co-authored-by: Thomas Rausch <thomas@thrau.at> Co-authored-by: Macwan Nevil <macnev2013@gmail.com> Co-authored-by: Dominik Schubert <dominik.schubert91@gmail.com> Co-authored-by: Gonzalo Saad <saad.gonzalo.ale@gmail.com> Co-authored-by: Mohit Alonja <monty16597@users.noreply.github.com> Co-authored-by: Miguel Gagliardo <migag9@gmail.com> Co-authored-by: Bert Blommers <info@bertblommers.nl>
540 lines
18 KiB
Python
540 lines
18 KiB
Python
import pytest
|
|
|
|
from moto.dynamodb2.exceptions import (
|
|
AttributeIsReservedKeyword,
|
|
ExpressionAttributeValueNotDefined,
|
|
AttributeDoesNotExist,
|
|
ExpressionAttributeNameNotDefined,
|
|
IncorrectOperandType,
|
|
InvalidUpdateExpressionInvalidDocumentPath,
|
|
EmptyKeyAttributeException,
|
|
)
|
|
from moto.dynamodb2.models import Item, DynamoType
|
|
from moto.dynamodb2.parsing.ast_nodes import (
|
|
NodeDepthLeftTypeFetcher,
|
|
UpdateExpressionSetAction,
|
|
DDBTypedValue,
|
|
)
|
|
from moto.dynamodb2.parsing.expressions import UpdateExpressionParser
|
|
from moto.dynamodb2.parsing.validators import UpdateExpressionValidator
|
|
|
|
|
|
def test_valid_update_expression(table):
|
|
update_expression = "set forum_name=:NewName, forum_type=:NewType"
|
|
update_expression_values = {
|
|
":NewName": {"S": "AmazingForum"},
|
|
":NewType": {"S": "BASIC"},
|
|
}
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "forum_name"}),
|
|
hash_key_type="TYPE",
|
|
range_key=DynamoType({"S": "forum_type"}),
|
|
range_key_type="TYPE",
|
|
attrs={"forum_name": {"S": "hello"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=update_expression_values,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
|
|
|
|
def test_validation_of_empty_string_key_val(table):
|
|
with pytest.raises(EmptyKeyAttributeException):
|
|
update_expression = "set forum_name=:NewName"
|
|
update_expression_values = {":NewName": {"S": ""}}
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "forum_name"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"forum_name": {"S": "hello"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=update_expression_values,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
|
|
|
|
def test_validation_of_update_expression_with_keyword(table):
|
|
try:
|
|
update_expression = "SET myNum = path + :val"
|
|
update_expression_values = {":val": {"N": "3"}}
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "path": {"N": "3"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=update_expression_values,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "No exception raised"
|
|
except AttributeIsReservedKeyword as e:
|
|
assert e.keyword == "path"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"update_expression", ["SET a = #b + :val2", "SET a = :val2 + #b",],
|
|
)
|
|
def test_validation_of_a_set_statement_with_incorrect_passed_value(
|
|
update_expression, table
|
|
):
|
|
"""
|
|
By running permutations it shows that values are replaced prior to resolving attributes.
|
|
|
|
An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression:
|
|
An expression attribute value used in expression is not defined; attribute value: :val2
|
|
"""
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "b": {"N": "3"}},
|
|
)
|
|
try:
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names={"#b": "ok"},
|
|
expression_attribute_values={":val": {"N": "3"}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
except ExpressionAttributeValueNotDefined as e:
|
|
assert e.attribute_value == ":val2"
|
|
|
|
|
|
def test_validation_of_update_expression_with_attribute_that_does_not_exist_in_item(
|
|
table,
|
|
):
|
|
"""
|
|
When an update expression tries to get an attribute that does not exist it must throw the appropriate exception.
|
|
|
|
An error occurred (ValidationException) when calling the UpdateItem operation:
|
|
The provided expression refers to an attribute that does not exist in the item
|
|
"""
|
|
try:
|
|
update_expression = "SET a = nonexistent"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "path": {"N": "3"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "No exception raised"
|
|
except AttributeDoesNotExist:
|
|
assert True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"update_expression", ["SET a = #c", "SET a = #c + #d",],
|
|
)
|
|
def test_validation_of_update_expression_with_attribute_name_that_is_not_defined(
|
|
update_expression, table,
|
|
):
|
|
"""
|
|
When an update expression tries to get an attribute name that is not provided it must throw an exception.
|
|
|
|
An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression:
|
|
An expression attribute name used in the document path is not defined; attribute name: #c
|
|
"""
|
|
try:
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "path": {"N": "3"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names={"#b": "ok"},
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "No exception raised"
|
|
except ExpressionAttributeNameNotDefined as e:
|
|
assert e.not_defined_attribute_name == "#c"
|
|
|
|
|
|
def test_validation_of_if_not_exists_not_existing_invalid_replace_value(table):
|
|
try:
|
|
update_expression = "SET a = if_not_exists(b, a.c)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "a": {"S": "A"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "No exception raised"
|
|
except AttributeDoesNotExist:
|
|
assert True
|
|
|
|
|
|
def get_first_node_of_type(ast, node_type):
|
|
return next(NodeDepthLeftTypeFetcher(node_type, ast))
|
|
|
|
|
|
def get_set_action_value(ast):
|
|
"""
|
|
Helper that takes an AST and gets the first UpdateExpressionSetAction and retrieves the value of that action.
|
|
This should only be called on validated expressions.
|
|
Args:
|
|
ast(Node):
|
|
|
|
Returns:
|
|
DynamoType: The DynamoType object representing the Dynamo value.
|
|
"""
|
|
set_action = get_first_node_of_type(ast, UpdateExpressionSetAction)
|
|
typed_value = set_action.children[1]
|
|
assert isinstance(typed_value, DDBTypedValue)
|
|
dynamo_value = typed_value.children[0]
|
|
assert isinstance(dynamo_value, DynamoType)
|
|
return dynamo_value
|
|
|
|
|
|
def test_validation_of_if_not_exists_not_existing_value(table):
|
|
update_expression = "SET a = if_not_exists(b, a)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "a": {"S": "A"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"S": "A"})
|
|
|
|
|
|
def test_validation_of_if_not_exists_with_existing_attribute_should_return_attribute(
|
|
table,
|
|
):
|
|
update_expression = "SET a = if_not_exists(b, a)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "a": {"S": "A"}, "b": {"S": "B"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"S": "B"})
|
|
|
|
|
|
def test_validation_of_if_not_exists_with_existing_attribute_should_return_value(table):
|
|
update_expression = "SET a = if_not_exists(b, :val)"
|
|
update_expression_values = {":val": {"N": "4"}}
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "b": {"N": "3"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=update_expression_values,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"N": "3"})
|
|
|
|
|
|
def test_validation_of_if_not_exists_with_non_existing_attribute_should_return_value(
|
|
table,
|
|
):
|
|
update_expression = "SET a = if_not_exists(b, :val)"
|
|
update_expression_values = {":val": {"N": "4"}}
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=update_expression_values,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"N": "4"})
|
|
|
|
|
|
def test_validation_of_sum_operation(table):
|
|
update_expression = "SET a = a + b"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "a": {"N": "3"}, "b": {"N": "4"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"N": "7"})
|
|
|
|
|
|
def test_validation_homogeneous_list_append_function(table):
|
|
update_expression = "SET ri = list_append(ri, :vals)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "ri": {"L": [{"S": "i1"}, {"S": "i2"}]}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":vals": {"L": [{"S": "i3"}, {"S": "i4"}]}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType(
|
|
{"L": [{"S": "i1"}, {"S": "i2"}, {"S": "i3"}, {"S": "i4"}]}
|
|
)
|
|
|
|
|
|
def test_validation_hetereogenous_list_append_function(table):
|
|
update_expression = "SET ri = list_append(ri, :vals)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "ri": {"L": [{"S": "i1"}, {"S": "i2"}]}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":vals": {"L": [{"N": "3"}]}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"L": [{"S": "i1"}, {"S": "i2"}, {"N": "3"}]})
|
|
|
|
|
|
def test_validation_list_append_function_with_non_list_arg(table):
|
|
"""
|
|
Must error out:
|
|
Invalid UpdateExpression: Incorrect operand type for operator or function;
|
|
operator or function: list_append, operand type: S'
|
|
Returns:
|
|
|
|
"""
|
|
try:
|
|
update_expression = "SET ri = list_append(ri, :vals)"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "ri": {"L": [{"S": "i1"}, {"S": "i2"}]}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":vals": {"S": "N"}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
except IncorrectOperandType as e:
|
|
assert e.operand_type == "S"
|
|
assert e.operator_or_function == "list_append"
|
|
|
|
|
|
def test_sum_with_incompatible_types(table):
|
|
"""
|
|
Must error out:
|
|
Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: +, operand type: S'
|
|
Returns:
|
|
|
|
"""
|
|
try:
|
|
update_expression = "SET ri = :val + :val2"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "ri": {"L": [{"S": "i1"}, {"S": "i2"}]}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":val": {"S": "N"}, ":val2": {"N": "3"}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
except IncorrectOperandType as e:
|
|
assert e.operand_type == "S"
|
|
assert e.operator_or_function == "+"
|
|
|
|
|
|
def test_validation_of_subraction_operation(table):
|
|
update_expression = "SET ri = :val - :val2"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "1"}, "a": {"N": "3"}, "b": {"N": "4"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":val": {"N": "1"}, ":val2": {"N": "3"}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"N": "-2"})
|
|
|
|
|
|
def test_cannot_index_into_a_string(table):
|
|
"""
|
|
Must error out:
|
|
The document path provided in the update expression is invalid for update'
|
|
"""
|
|
try:
|
|
update_expression = "set itemstr[1]=:Item"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "foo2"}, "itemstr": {"S": "somestring"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values={":Item": {"S": "string_update"}},
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "Must raise exception"
|
|
except InvalidUpdateExpressionInvalidDocumentPath:
|
|
assert True
|
|
|
|
|
|
def test_validation_set_path_does_not_need_to_be_resolvable_when_setting_a_new_attribute(
|
|
table,
|
|
):
|
|
"""If this step just passes we are happy enough"""
|
|
update_expression = "set d=a"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "foo2"}, "a": {"N": "3"}},
|
|
)
|
|
validated_ast = UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
dynamo_value = get_set_action_value(validated_ast)
|
|
assert dynamo_value == DynamoType({"N": "3"})
|
|
|
|
|
|
def test_validation_set_path_does_not_need_to_be_resolvable_but_must_be_creatable_when_setting_a_new_attribute(
|
|
table,
|
|
):
|
|
try:
|
|
update_expression = "set d.e=a"
|
|
update_expression_ast = UpdateExpressionParser.make(update_expression)
|
|
item = Item(
|
|
hash_key=DynamoType({"S": "id"}),
|
|
hash_key_type="TYPE",
|
|
range_key=None,
|
|
range_key_type=None,
|
|
attrs={"id": {"S": "foo2"}, "a": {"N": "3"}},
|
|
)
|
|
UpdateExpressionValidator(
|
|
update_expression_ast,
|
|
expression_attribute_names=None,
|
|
expression_attribute_values=None,
|
|
item=item,
|
|
table=table,
|
|
).validate()
|
|
assert False, "Must raise exception"
|
|
except InvalidUpdateExpressionInvalidDocumentPath:
|
|
assert True
|