Skip to content

Commit 058bd78

Browse files
wip
1 parent 11c7a2b commit 058bd78

File tree

18 files changed

+279
-52
lines changed

18 files changed

+279
-52
lines changed

DynamoDbEncryption/runtimes/python/src/aws_database_encryption_sdk/encryptor/table.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,22 @@ def scan(self, **kwargs):
148148
return response
149149

150150
def query(self, **kwargs):
151+
print(f"query {kwargs=}")
151152
input_transform_input = self._resource_shape_to_client_shape_converter.query_request(kwargs)
153+
154+
print(f"query {input_transform_input=}")
152155
input_transform_output = self._transformer.query_input_transform(
153156
QueryInputTransformInput(
154157
sdk_input = input_transform_input
155158
)
156159
).transformed_input
157160

161+
print(f"query {input_transform_output=}")
162+
158163
sdk_input = self._client_shape_to_resource_shape_converter.query_request(input_transform_output)
159164

165+
print(f"query {sdk_input=}")
166+
160167
sdk_output = self._table.query(**sdk_input)
161168

162169
output_transform_input = self._resource_shape_to_client_shape_converter.query_response(sdk_output)

DynamoDbEncryption/runtimes/python/src/aws_database_encryption_sdk/internal/boto3_conversions.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ class BotoInterfaceShapeConverter(ABC):
2121
But the structure modelling is the same for both directions, and is expressed here.
2222
Implementations of this class should implement the abstract methods to perform the transformations.
2323
"""
24-
def key_condition_expression(self, key_condition_expression):
25-
return self.expression(key_condition_expression)
24+
# def key_condition_expression(self, key_condition_expression, expression_attribute_names, expression_attribute_values):
25+
# return self.expression(key_condition_expression, expression_attribute_names, expression_attribute_values)
2626

27-
def filter_expression(self, filter_expression):
28-
return self.expression(filter_expression)
27+
# def filter_expression(self, filter_expression, expression_attribute_names, expression_attribute_values):
28+
# return self.expression(filter_expression, expression_attribute_names, expression_attribute_values)
2929

3030
@abstractmethod
31-
def expression(self, condition_expression):
31+
def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
3232
pass
3333

3434
def key_conditions(self, key_conditions):
@@ -128,7 +128,7 @@ def scan_request(self, scan_request):
128128
if "ExclusiveStartKey" in scan_request:
129129
scan_request["ExclusiveStartKey"] = self.exclusive_start_key(scan_request["ExclusiveStartKey"])
130130
if "FilterExpression" in scan_request:
131-
scan_request["FilterExpression"] = self.filter_expression(scan_request["FilterExpression"])
131+
self._handle_expression_with_expression_attributes(scan_request, "FilterExpression")
132132
if "ExpressionAttributeValues" in scan_request:
133133
scan_request["ExpressionAttributeValues"] = self.expression_attribute_values(scan_request["ExpressionAttributeValues"])
134134
return scan_request
@@ -140,6 +140,18 @@ def scan_response(self, scan_response):
140140
scan_response["LastEvaluatedKey"] = self.last_evaluated_key(scan_response["LastEvaluatedKey"])
141141
return scan_response
142142

143+
def _handle_expression_with_expression_attributes(self, request, expression_key):
144+
# Pass the resource-formatted expression attribute values to the converter
145+
if "ExpressionAttributeValues" in request:
146+
expression_attribute_values = request["ExpressionAttributeValues"]
147+
else:
148+
expression_attribute_values = {}
149+
if "ExpressionAttributeNames" in request:
150+
expression_attribute_names = request["ExpressionAttributeNames"]
151+
else:
152+
expression_attribute_names = {}
153+
request[expression_key] = self.expression(request[expression_key], expression_attribute_names, expression_attribute_values)
154+
143155
def query_request(self, query_request):
144156
if "KeyConditions" in query_request:
145157
query_request["KeyConditions"] = self.key_conditions(query_request["KeyConditions"])
@@ -148,9 +160,9 @@ def query_request(self, query_request):
148160
if "ExclusiveStartKey" in query_request:
149161
query_request["ExclusiveStartKey"] = self.exclusive_start_key(query_request["ExclusiveStartKey"])
150162
if "FilterExpression" in query_request:
151-
query_request["FilterExpression"] = self.filter_expression(query_request["FilterExpression"])
163+
self._handle_expression_with_expression_attributes(query_request, "FilterExpression")
152164
if "KeyConditionExpression" in query_request:
153-
query_request["KeyConditionExpression"] = self.key_condition_expression(query_request["KeyConditionExpression"])
165+
self._handle_expression_with_expression_attributes(query_request, "KeyConditionExpression")
154166
if "ExpressionAttributeValues" in query_request:
155167
query_request["ExpressionAttributeValues"] = self.expression_attribute_values(query_request["ExpressionAttributeValues"])
156168
return query_request

DynamoDbEncryption/runtimes/python/src/aws_database_encryption_sdk/internal/client_to_resource.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ def scan_request(self, scan_request):
3030
del out["TableName"]
3131
return out
3232

33-
def expression(self, condition_expression):
33+
def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
3434
# Tables accept the same format as the client (string).
3535
return condition_expression

DynamoDbEncryption/runtimes/python/src/aws_database_encryption_sdk/internal/condition_expression_builder.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ConditionBase,
99
BuiltConditionExpression,
1010
AttributeBase,
11+
Key
1112
)
1213
import re
1314

@@ -60,6 +61,7 @@ def build_expression(self, condition, attribute_name_placeholders, attribute_val
6061
attribute_value_placeholders,
6162
is_key_condition=is_key_condition,
6263
)
64+
print(f"BuiltConditionExpression {condition_expression=}")
6365
return BuiltConditionExpression(
6466
condition_expression=condition_expression,
6567
attribute_name_placeholders=attribute_name_placeholders,
@@ -138,37 +140,61 @@ def _build_name_placeholder(self, value, attribute_name_placeholders):
138140
placeholder_format = ATTR_NAME_REGEX.sub('%s', attribute_name)
139141
str_format_args = []
140142
for part in attribute_name_parts:
141-
name_placeholder = self._get_name_placeholder()
142-
self._name_count += 1
143-
str_format_args.append(name_placeholder)
144-
# Add the placeholder and value to dictionary of name placeholders.
145-
attribute_name_placeholders[name_placeholder] = part
143+
# If the the name is already an AttributeName, use it. Don't make a new placeholder.
144+
if part in attribute_name_placeholders:
145+
str_format_args.append(part)
146+
else:
147+
name_placeholder = self._get_name_placeholder()
148+
self._name_count += 1
149+
str_format_args.append(name_placeholder)
150+
# Add the placeholder and value to dictionary of name placeholders.
151+
attribute_name_placeholders[name_placeholder] = part
146152
# Replace the temporary placeholders with the designated placeholders.
147153
return placeholder_format % tuple(str_format_args)
148154

149155
def _build_value_placeholder(
150156
self, value, attribute_value_placeholders, has_grouped_values=False
151157
):
158+
print(f"{attribute_value_placeholders=}")
152159
# If the values are grouped, we need to add a placeholder for
153160
# each element inside of the actual value.
161+
162+
# Also, you can define a grouped value with a colon here.
163+
# If it's a colon, it's not a grouped value for the sake of this logic.
164+
# Treat it as an "else" case.
154165
if has_grouped_values:
155166
placeholder_list = []
167+
# If it's a pre-defined grouped attribute, don't attempt to unpack it as if it were
156168
for v in value:
157-
value_placeholder = self._get_value_placeholder()
158-
self._value_count += 1
159-
placeholder_list.append(value_placeholder)
160-
attribute_value_placeholders[value_placeholder] = v
169+
print(f"v1 {v=}")
170+
# If the value is already an AttributeValue, reuse it. Don't make a new placeholder.
171+
if v in attribute_value_placeholders:
172+
print("in")
173+
placeholder_list.append(v)
174+
else:
175+
print("not in")
176+
value_placeholder = self._get_value_placeholder()
177+
self._value_count += 1
178+
placeholder_list.append(value_placeholder)
179+
attribute_value_placeholders[value_placeholder] = v
161180
# Assuming the values are grouped by parenthesis.
162181
# IN is the currently the only one that uses this so it maybe
163182
# needed to be changed in future.
164183
return '(' + ', '.join(placeholder_list) + ')'
165184
# Otherwise, treat the value as a single value that needs only
166185
# one placeholder.
167186
else:
168-
value_placeholder = self._get_value_placeholder()
169-
self._value_count += 1
170-
attribute_value_placeholders[value_placeholder] = value
171-
return value_placeholder
187+
print(f"v2 {value=}")
188+
# If the value is already an AttributeValue, reuse it. Don't make a new placeholder.
189+
if value in attribute_value_placeholders:
190+
print("in")
191+
return value
192+
else:
193+
print("not in")
194+
value_placeholder = self._get_value_placeholder()
195+
self._value_count += 1
196+
attribute_value_placeholders[value_placeholder] = value
197+
return value_placeholder
172198

173199

174200
# class InternalDBESDKDynamoDBConditionExpressionBuilder:

DynamoDbEncryption/runtimes/python/src/aws_database_encryption_sdk/internal/resource_to_client.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from aws_database_encryption_sdk.transform import dict_to_ddb
22
from .boto3_conversions import BotoInterfaceShapeConverter
33
from aws_database_encryption_sdk.internal.condition_expression_builder import InternalDBESDKDynamoDBConditionExpressionBuilder
4-
from boto3.dynamodb.conditions import ConditionBase, BuiltConditionExpression
4+
from boto3.dynamodb.conditions import BuiltConditionExpression
5+
from boto3.dynamodb.types import TypeSerializer
56

67
class ResourceShapeToClientShapeConverter(BotoInterfaceShapeConverter):
78

@@ -34,8 +35,8 @@ def key_to_attribute_value_map(self, key_to_attribute_value):
3435
return dict_to_ddb(key_to_attribute_value)
3536

3637
def attribute_value(self, attribute_value):
37-
# TODO: optimize
38-
return list(dict_to_ddb({"throwaway_key": attribute_value}).values())[0]
38+
serializer = TypeSerializer()
39+
return serializer.serialize(attribute_value)
3940

4041
def put_item_request(self, put_item_request):
4142
if not self.table_name:
@@ -72,12 +73,10 @@ def scan_request(self, scan_request):
7273
self._unpack_built_condition_expression(super_conversion, "FilterExpression")
7374
return super_conversion
7475

75-
def expression(self, condition_expression):
76+
def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
7677
# Expressions provided to tables can be Condition objects, which need to be converted to strings.
77-
if condition_expression.__module__ == "boto3.dynamodb.conditions":
78-
out = self.expression_builder.build_expression(condition_expression, {}, {})
79-
print(f"{condition_expression=}")
80-
print(f"{out=}")
78+
if hasattr(condition_expression, "__module__") and condition_expression.__module__ == "boto3.dynamodb.conditions":
79+
out = self.expression_builder.build_expression(condition_expression, expression_attribute_names, expression_attribute_values)
8180
return out
8281
# Expressions provided to tables can also already be string-like.
8382
# Assume the user has provided something string-like, and let Smithy-Python/DBESDK internals raise exceptions if not.

Examples/runtimes/python/DynamoDBEncryption/src/create_keystore_key_example.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
13
from aws_cryptographic_material_providers.keystore.models import (
24
KMSConfigurationKmsKeyArn,
35
CreateKeyInput,

Examples/runtimes/python/DynamoDBEncryption/src/encrypted_client.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@
3434
DynamoDbTableEncryptionConfig,
3535
DynamoDbTablesEncryptionConfig,
3636
)
37-
from aws_database_encryption_sdk.smithygenerated.aws_cryptography_dbencryptionsdk_dynamodb_transforms.models import (
38-
GetItemOutputTransformInput,
39-
PutItemInputTransformInput
40-
)
4137
from aws_database_encryption_sdk.encryptor.client import (
4238
EncryptedClient
4339
)
@@ -142,10 +138,13 @@ def encrypted_client_put_get_example(
142138
put_item_request = {
143139
"TableName": dynamodb_table_name,
144140
"Item": item_to_encrypt,
141+
"ReturnValuesOnConditionCheckFailure": "ALL_OLD",
145142
}
146143

147144
put_item_response = encrypted_client.put_item(**put_item_request)
148145

146+
print(f"{put_item_response=}")
147+
149148
# Demonstrate that PutItem succeeded
150149
assert put_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
151150

@@ -166,5 +165,7 @@ def encrypted_client_put_get_example(
166165

167166
# Demonstrate that GetItem succeeded
168167
assert get_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
169-
assert get_item_response["Item"] == item_to_encrypt
170-
assert get_item_response["Item"]["attribute1"] == "encrypt and sign me!"
168+
print(f"{item_to_encrypt=}")
169+
print(f"{get_item_response['Item']=}")
170+
# assert get_item_response["Item"] == item_to_encrypt
171+
assert get_item_response["Item"]["attribute1"] == {"S" : "encrypt and sign me!"}

Examples/runtimes/python/DynamoDBEncryption/src/searchable_encryption/complex_example/beacon_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
13
from aws_cryptographic_material_providers.keystore.client import KeyStore
24
from aws_cryptographic_material_providers.keystore.config import KeyStoreConfig
35
from aws_cryptographic_material_providers.keystore.models import (

Examples/runtimes/python/DynamoDBEncryption/src/searchable_encryption/complex_example/client/example.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
13
import boto3
24
from ..beacon_config import setup_beacon_config
35
from aws_database_encryption_sdk.encryptor.client import EncryptedClient

Examples/runtimes/python/DynamoDBEncryption/src/searchable_encryption/complex_example/client/put_requests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
13
def put_all_meeting_items_to_table(ddb_table_name, ddb):
24
meetings = [
35
{

0 commit comments

Comments
 (0)