Skip to content

Commit 2ed1a0e

Browse files
author
Lucas McDonald
committed
m
1 parent 256c64b commit 2ed1a0e

File tree

10 files changed

+5504
-566
lines changed

10 files changed

+5504
-566
lines changed
Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,91 @@
11
from aws_database_encryption_sdk.transform import ddb_to_dict
22
from .boto3_conversions import BotoInterfaceShapeConverter
3+
from aws_cryptography_internal_dynamodb.smithygenerated.com_amazonaws_dynamodb.boto3_conversions import InternalBoto3DynamoDBFormatConverter
4+
from boto3.dynamodb.types import TypeDeserializer
35

4-
class ClientShapeToResourceShapeConverter(BotoInterfaceShapeConverter):
6+
class ClientShapeToResourceShapeConverter:
57

68
def __init__(self, delete_table_name = True):
79
self.delete_table_name = delete_table_name
10+
self.boto3_converter = InternalBoto3DynamoDBFormatConverter(
11+
item_handler=TypeDeserializer().deserialize,
12+
condition_handler=self.condition_handler
13+
)
814

9-
def item(self, item):
10-
return ddb_to_dict(item)
11-
12-
def attribute_value(self, attribute_value):
13-
# TODO: optimize
14-
return list(ddb_to_dict({"throwaway_key": attribute_value}).values())[0]
15+
def condition_handler(self, expression_key, request):
16+
try:
17+
identity_names = request["ExpressionAttributeNames"]
18+
except KeyError:
19+
identity_names = {}
20+
21+
try:
22+
identity_values = request["ExpressionAttributeValues"]
23+
except KeyError:
24+
identity_values = {}
25+
return request[expression_key], identity_names, identity_values
1526

1627
def put_item_request(self, put_item_request):
17-
out = super().put_item_request(put_item_request)
28+
# out = super().put_item_request(put_item_request)
29+
out = self.boto3_converter.PutItemInput(put_item_request)
1830
if self.delete_table_name:
1931
del out["TableName"]
2032
return out
2133

2234
def get_item_request(self, get_item_request):
23-
out = super().get_item_request(get_item_request)
35+
# out = super().get_item_request(get_item_request)
36+
out = self.boto3_converter.GetItemInput(get_item_request)
2437
if self.delete_table_name:
2538
del out["TableName"]
2639
return out
2740

2841
def query_request(self, query_request):
29-
out = super().query_request(query_request)
42+
# out = super().query_request(query_request)
43+
out = self.boto3_converter.QueryInput(query_request)
3044
if self.delete_table_name:
3145
del out["TableName"]
3246
return out
3347

3448
def scan_request(self, scan_request):
35-
out = super().scan_request(scan_request)
49+
# out = super().scan_request(scan_request)
50+
out = self.boto3_converter.ScanInput(scan_request)
3651
if self.delete_table_name:
3752
del out["TableName"]
3853
return out
3954

40-
def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
41-
# Tables accept the same format as the client (string).
42-
return condition_expression
55+
def transact_get_items_request(self, transact_get_items_request):
56+
return self.boto3_converter.TransactGetItemsInput(transact_get_items_request)
57+
58+
def transact_get_items_response(self, transact_get_items_response):
59+
return self.boto3_converter.TransactGetItemsOutput(transact_get_items_response)
60+
61+
def transact_write_items_request(self, transact_write_items_request):
62+
return self.boto3_converter.TransactWriteItemsInput(transact_write_items_request)
63+
64+
def transact_write_items_response(self, transact_write_items_response):
65+
return self.boto3_converter.TransactWriteItemsOutput(transact_write_items_response)
66+
67+
def batch_get_item_request(self, batch_get_item_request):
68+
return self.boto3_converter.BatchGetItemInput(batch_get_item_request)
69+
70+
def batch_get_item_response(self, batch_get_item_response):
71+
return self.boto3_converter.BatchGetItemOutput(batch_get_item_response)
72+
73+
def batch_write_item_request(self, batch_write_item_request):
74+
return self.boto3_converter.BatchWriteItemInput(batch_write_item_request)
75+
76+
def batch_write_item_response(self, batch_write_item_response):
77+
return self.boto3_converter.BatchWriteItemOutput(batch_write_item_response)
78+
79+
def scan_response(self, scan_response):
80+
return self.boto3_converter.ScanOutput(scan_response)
81+
82+
def query_response(self, query_response):
83+
return self.boto3_converter.QueryOutput(query_response)
84+
85+
def get_item_response(self, get_item_response):
86+
return self.boto3_converter.GetItemOutput(get_item_response)
87+
88+
def put_item_response(self, put_item_response):
89+
return self.boto3_converter.PutItemOutput(put_item_response)
90+
91+

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

Lines changed: 146 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
from aws_database_encryption_sdk.internal.condition_expression_builder import InternalDBESDKDynamoDBConditionExpressionBuilder
44
from boto3.dynamodb.conditions import BuiltConditionExpression
55
from boto3.dynamodb.types import TypeSerializer
6+
from aws_cryptography_internal_dynamodb.smithygenerated.com_amazonaws_dynamodb.boto3_conversions import InternalBoto3DynamoDBFormatConverter
67

7-
class ResourceShapeToClientShapeConverter(BotoInterfaceShapeConverter):
8+
class ResourceShapeToClientShapeConverter:
89

910
def __init__(self, table_name = None):
11+
self.boto3_converter = InternalBoto3DynamoDBFormatConverter(
12+
item_handler=TypeSerializer().serialize,
13+
condition_handler=self._unpack_built_condition_expression2
14+
)
1015
self.table_name = table_name
1116
self.expression_builder = InternalDBESDKDynamoDBConditionExpressionBuilder()
1217

@@ -20,14 +25,44 @@ def _unpack_built_condition_expression(self, request_to_update, expression_key):
2025
except KeyError:
2126
request_to_update["ExpressionAttributeNames"] = attribute_names_from_built_expression
2227
# BuiltConditionExpression stores values in resource format; convert to client format before joining
23-
attribute_values_from_built_expression = super().expression_attribute_values(
24-
built_condition_expression.attribute_value_placeholders
25-
)
28+
attribute_values_from_built_expression = dict_to_ddb(built_condition_expression.attribute_value_placeholders)
2629
try:
2730
request_to_update["ExpressionAttributeValues"] = request_to_update["ExpressionAttributeValues"] | attribute_values_from_built_expression
2831
except KeyError:
2932
request_to_update["ExpressionAttributeValues"] = attribute_values_from_built_expression
3033

34+
def _unpack_built_condition_expression2(self, expression_key, request):
35+
condition_expression = request[expression_key]
36+
try:
37+
existing_expression_attribute_names = request["ExpressionAttributeNames"]
38+
except KeyError:
39+
existing_expression_attribute_names = {}
40+
try:
41+
existing_expression_attribute_values = request["ExpressionAttributeValues"]
42+
except KeyError:
43+
existing_expression_attribute_values = {}
44+
45+
if hasattr(condition_expression, "__module__") and condition_expression.__module__ == "boto3.dynamodb.conditions":
46+
built_condition_expression = self.expression_builder.build_expression(condition_expression, existing_expression_attribute_names, existing_expression_attribute_values)
47+
else:
48+
return condition_expression, existing_expression_attribute_names, existing_expression_attribute_values
49+
50+
expression_str = built_condition_expression.condition_expression
51+
attribute_names_from_built_expression = built_condition_expression.attribute_name_placeholders
52+
# Join any placeholder ExpressionAttributeNames with any other ExpressionAttributeNames
53+
try:
54+
out_names = request["ExpressionAttributeNames"] | attribute_names_from_built_expression
55+
except KeyError:
56+
out_names = attribute_names_from_built_expression
57+
# BuiltConditionExpression stores values in resource format; convert to client format before joining
58+
attribute_values_from_built_expression = dict_to_ddb(built_condition_expression.attribute_value_placeholders)
59+
try:
60+
out_values = request["ExpressionAttributeValues"] | attribute_values_from_built_expression
61+
except KeyError:
62+
out_values = attribute_values_from_built_expression
63+
64+
return expression_str, out_names, out_values
65+
3166
def item(self, item):
3267
return dict_to_ddb(item)
3368

@@ -38,84 +73,134 @@ def attribute_value(self, attribute_value):
3873
serializer = TypeSerializer()
3974
return serializer.serialize(attribute_value)
4075

76+
# def put_item_request(self, put_item_request):
77+
# if not self.table_name:
78+
# raise ValueError("Table name must be provided to ResourceShapeToClientShapeConverter to use put_item")
79+
# put_item_request["TableName"] = self.table_name
80+
# super_conversion = super().put_item_request(put_item_request)
81+
# if "ConditionExpression" in super_conversion and isinstance(super_conversion["ConditionExpression"], BuiltConditionExpression):
82+
# self._unpack_built_condition_expression(super_conversion, "ConditionExpression")
83+
# return super_conversion
84+
4185
def put_item_request(self, put_item_request):
4286
if not self.table_name:
4387
raise ValueError("Table name must be provided to ResourceShapeToClientShapeConverter to use put_item")
4488
put_item_request["TableName"] = self.table_name
45-
super_conversion = super().put_item_request(put_item_request)
46-
if "ConditionExpression" in super_conversion and isinstance(super_conversion["ConditionExpression"], BuiltConditionExpression):
47-
self._unpack_built_condition_expression(super_conversion, "ConditionExpression")
48-
return super_conversion
89+
return self.boto3_converter.PutItemInput(put_item_request)
90+
# if "ConditionExpression" in super_conversion and isinstance(super_conversion["ConditionExpression"], BuiltConditionExpression):
91+
# self._unpack_built_condition_expression(super_conversion, "ConditionExpression")
4992

5093
def get_item_request(self, get_item_request):
5194
if not self.table_name:
5295
raise ValueError("Table name must be provided to ResourceShapeToClientShapeConverter to use get_item")
5396
get_item_request["TableName"] = self.table_name
54-
return super().get_item_request(get_item_request)
97+
# return super().get_item_request(get_item_request)
98+
return self.boto3_converter.GetItemInput(get_item_request)
5599

56100
def query_request(self, query_request):
57101
if not self.table_name:
58102
raise ValueError("Table name must be provided to ResourceShapeToClientShapeConverter to use query")
59103
query_request["TableName"] = self.table_name
60-
super_conversion = super().query_request(query_request)
61-
if "KeyConditionExpression" in super_conversion and isinstance(super_conversion["KeyConditionExpression"], BuiltConditionExpression):
62-
self._unpack_built_condition_expression(super_conversion, "KeyConditionExpression")
63-
if "FilterExpression" in super_conversion and isinstance(super_conversion["FilterExpression"], BuiltConditionExpression):
64-
self._unpack_built_condition_expression(super_conversion, "FilterExpression")
65-
return super_conversion
104+
# super_conversion = super().query_request(query_request)
105+
# if "KeyConditionExpression" in super_conversion and isinstance(super_conversion["KeyConditionExpression"], BuiltConditionExpression):
106+
# self._unpack_built_condition_expression(super_conversion, "KeyConditionExpression")
107+
# if "FilterExpression" in super_conversion and isinstance(super_conversion["FilterExpression"], BuiltConditionExpression):
108+
# self._unpack_built_condition_expression(super_conversion, "FilterExpression")
109+
# return super_conversion
110+
return self.boto3_converter.QueryInput(query_request)
66111

67112
def scan_request(self, scan_request):
68113
if not self.table_name:
69114
raise ValueError("Table name must be provided to ResourceShapeToClientShapeConverter to use scan")
70115
scan_request["TableName"] = self.table_name
71-
super_conversion = super().scan_request(scan_request)
72-
if "FilterExpression" in super_conversion and isinstance(super_conversion["FilterExpression"], BuiltConditionExpression):
73-
self._unpack_built_condition_expression(super_conversion, "FilterExpression")
74-
return super_conversion
116+
# super_conversion = super().scan_request(scan_request)
117+
# if "FilterExpression" in super_conversion and isinstance(super_conversion["FilterExpression"], BuiltConditionExpression):
118+
# self._unpack_built_condition_expression(super_conversion, "FilterExpression")
119+
# return super_conversion
120+
return self.boto3_converter.ScanInput(scan_request)
75121

76-
def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
77-
# Expressions provided to tables can be Condition objects, which need to be converted to strings.
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)
80-
return out
81-
# Expressions provided to tables can also already be string-like.
82-
# Assume the user has provided something string-like, and let Smithy-Python/DBESDK internals raise exceptions if not.
83-
print(f"probably a string: {condition_expression=}")
84-
return condition_expression
122+
def transact_get_items_request(self, transact_get_items_request):
123+
return self.boto3_converter.TransactGetItemsInput(transact_get_items_request)
124+
125+
def transact_get_items_response(self, transact_get_items_response):
126+
return self.boto3_converter.TransactGetItemsOutput(transact_get_items_response)
127+
128+
def transact_write_items_request(self, transact_write_items_request):
129+
return self.boto3_converter.TransactWriteItemsInput(transact_write_items_request)
130+
131+
def transact_write_items_response(self, transact_write_items_response):
132+
return self.boto3_converter.TransactWriteItemsOutput(transact_write_items_response)
133+
134+
def batch_get_item_request(self, batch_get_item_request):
135+
return self.boto3_converter.BatchGetItemInput(batch_get_item_request)
136+
137+
def batch_get_item_response(self, batch_get_item_response):
138+
return self.boto3_converter.BatchGetItemOutput(batch_get_item_response)
139+
140+
def batch_write_item_request(self, batch_write_item_request):
141+
return self.boto3_converter.BatchWriteItemInput(batch_write_item_request)
142+
143+
def batch_write_item_response(self, batch_write_item_response):
144+
return self.boto3_converter.BatchWriteItemOutput(batch_write_item_response)
145+
146+
def scan_response(self, scan_response):
147+
return self.boto3_converter.ScanOutput(scan_response)
148+
149+
def query_response(self, query_response):
150+
return self.boto3_converter.QueryOutput(query_response)
151+
152+
def get_item_response(self, get_item_response):
153+
return self.boto3_converter.GetItemOutput(get_item_response)
154+
155+
def put_item_response(self, put_item_response):
156+
return self.boto3_converter.PutItemOutput(put_item_response)
157+
158+
159+
160+
161+
# def expression(self, condition_expression, expression_attribute_names, expression_attribute_values):
162+
# # Expressions provided to tables can be Condition objects, which need to be converted to strings.
163+
# if hasattr(condition_expression, "__module__") and condition_expression.__module__ == "boto3.dynamodb.conditions":
164+
# out = self.expression_builder.build_expression(condition_expression, expression_attribute_names, expression_attribute_values)
165+
# return out
166+
# # Expressions provided to tables can also already be string-like.
167+
# # Assume the user has provided something string-like, and let Smithy-Python/DBESDK internals raise exceptions if not.
168+
# print(f"probably a string: {condition_expression=}")
169+
# return condition_expression
85170

86-
def batch_write_item_request_items(self, tables):
87-
"""Transform a batch write request's items to DynamoDB format."""
88-
output_tables = {}
89-
table_names = list(tables.keys())
90-
for table_name in table_names:
91-
requests = tables[table_name]
92-
output_requests = []
93-
for request in requests:
94-
request_name_list = list(request.keys())
95-
if len(request_name_list) > 1:
96-
raise ValueError("Invalid JSON format")
97-
request_name = request_name_list[0]
98-
if request_name == "PutRequest":
99-
dict_request = dict_to_ddb(request[request_name]["Item"])
100-
boto3_request = {"Item": dict_request}
101-
elif request_name == "DeleteRequest":
102-
# Delete requests are based on Keys, which are expected to already be in DynamoDB JSON.
103-
# Only Items can be in Python dictionary JSON.
104-
boto3_request = request[request_name]
105-
else:
106-
raise ValueError(f"Unknown batch_write_items method key: {request_name}")
107-
output_requests.append({request_name: boto3_request})
108-
output_tables[table_name] = output_requests
109-
return output_tables
171+
# def batch_write_item_request_items(self, tables):
172+
# """Transform a batch write request's items to DynamoDB format."""
173+
# output_tables = {}
174+
# table_names = list(tables.keys())
175+
# for table_name in table_names:
176+
# requests = tables[table_name]
177+
# output_requests = []
178+
# for request in requests:
179+
# request_name_list = list(request.keys())
180+
# if len(request_name_list) > 1:
181+
# raise ValueError("Invalid JSON format")
182+
# request_name = request_name_list[0]
183+
# if request_name == "PutRequest":
184+
# dict_request = dict_to_ddb(request[request_name]["Item"])
185+
# boto3_request = {"Item": dict_request}
186+
# elif request_name == "DeleteRequest":
187+
# # Delete requests are based on Keys, which are expected to already be in DynamoDB JSON.
188+
# # Only Items can be in Python dictionary JSON.
189+
# boto3_request = request[request_name]
190+
# else:
191+
# raise ValueError(f"Unknown batch_write_items method key: {request_name}")
192+
# output_requests.append({request_name: boto3_request})
193+
# output_tables[table_name] = output_requests
194+
# return output_tables
110195

111-
def batch_get_request(self, **kwargs):
112-
"""Transform a batch get request to DynamoDB format."""
113-
dynamodb_input = kwargs.copy()
114-
tables = dynamodb_input["RequestItems"]
196+
# def batch_get_request(self, **kwargs):
197+
# """Transform a batch get request to DynamoDB format."""
198+
# dynamodb_input = kwargs.copy()
199+
# tables = dynamodb_input["RequestItems"]
115200

116-
for table_name, table_data in tables.items():
117-
dynamodb_input["RequestItems"][table_name]["Keys"] = [
118-
self.key_to_attribute_value_map(key) for key in table_data["Keys"]
119-
]
201+
# for table_name, table_data in tables.items():
202+
# dynamodb_input["RequestItems"][table_name]["Keys"] = [
203+
# self.key_to_attribute_value_map(key) for key in table_data["Keys"]
204+
# ]
120205

121-
return dynamodb_input
206+
# return dynamodb_input

0 commit comments

Comments
 (0)