Skip to content

Commit

Permalink
DynamoDB: Fix projection expression with binary attribute (#6816)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblommers committed Sep 15, 2023
1 parent 971e432 commit 2bae13b
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 1 deletion.
10 changes: 10 additions & 0 deletions moto/dynamodb/models/dynamo_type.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import copy
import decimal

Expand Down Expand Up @@ -195,6 +196,10 @@ def size(self) -> int:

def to_json(self) -> Dict[str, Any]:
# Returns a regular JSON object where the value can still be/contain a DynamoType
if self.is_binary():
# Binary data cannot be represented in JSON
# AWS returns a base64-encoded value - the SDK's then convert that back
return {self.type: base64.b64encode(self.value).decode("utf-8")}
return {self.type: self.value}

def to_regular_json(self) -> Dict[str, Any]:
Expand All @@ -212,6 +217,8 @@ def to_regular_json(self) -> Dict[str, Any]:
val.to_regular_json() if isinstance(val, DynamoType) else val
for val in value
]
if self.is_binary():
value = base64.b64decode(value)
return {self.type: value}

def compare(self, range_comparison: str, range_objs: List[Any]) -> bool:
Expand All @@ -236,6 +243,9 @@ def is_list(self) -> bool:
def is_map(self) -> bool:
return self.type == DDBType.MAP

def is_binary(self) -> bool:
return self.type == DDBType.BINARY

def same_type(self, other: "DynamoType") -> bool:
return self.type == other.type

Expand Down
2 changes: 1 addition & 1 deletion moto/dynamodb/models/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def dynamo_json_dump(dynamo_object: Any) -> str:


def bytesize(val: str) -> int:
return len(val.encode("utf-8"))
return len(val if isinstance(val, bytes) else val.encode("utf-8"))


def find_nested_key(
Expand Down
25 changes: 25 additions & 0 deletions tests/test_dynamodb/test_dynamodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import boto3
from boto3.dynamodb.conditions import Attr, Key
from boto3.dynamodb.types import Binary
import re
from moto import mock_dynamodb, settings
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
Expand Down Expand Up @@ -5727,6 +5728,30 @@ def test_projection_expression_execution_order():
)


@mock_dynamodb
def test_projection_expression_with_binary_attr():
dynamo_resource = boto3.resource("dynamodb", region_name="us-east-1")
dynamo_resource.create_table(
TableName="test",
AttributeDefinitions=[
{"AttributeName": "pk", "AttributeType": "S"},
{"AttributeName": "sk", "AttributeType": "S"},
],
KeySchema=[
{"AttributeName": "pk", "KeyType": "HASH"},
{"AttributeName": "sk", "KeyType": "RANGE"},
],
BillingMode="PAY_PER_REQUEST",
)
table = dynamo_resource.Table("test")
table.put_item(Item={"pk": "pk", "sk": "sk", "key": b"value\xbf"})
assert table.get_item(
Key={"pk": "pk", "sk": "sk"},
ExpressionAttributeNames={"#key": "key"},
ProjectionExpression="#key",
)["Item"] == {"key": Binary(b"value\xbf")}


@mock_dynamodb
def test_invalid_projection_expressions():
table_name = "test-projection-expressions-table"
Expand Down

0 comments on commit 2bae13b

Please sign in to comment.