Skip to content

Commit 321284c

Browse files
m
1 parent 05ea742 commit 321284c

File tree

7 files changed

+143
-147
lines changed

7 files changed

+143
-147
lines changed

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
)
1212
from aws_database_encryption_sdk.internal.resource_to_client import ResourceShapeToClientShapeConverter
1313
from aws_database_encryption_sdk.internal.client_to_resource import ClientShapeToResourceShapeConverter
14-
14+
from aws_database_encryption_sdk.smithygenerated.aws_cryptography_dbencryptionsdk_dynamodb_transforms.client import (
15+
DynamoDbEncryptionTransforms
16+
)
17+
from aws_database_encryption_sdk.encryptor.table import EncryptedTable
1518

1619
class EncryptedTablesCollectionManager:
1720

@@ -33,18 +36,20 @@ def __init__(
3336
):
3437
self._resource = resource
3538
self._encryption_config = encryption_config
39+
self._transformer = DynamoDbEncryptionTransforms(
40+
config = encryption_config
41+
)
3642
self._client_shape_to_resource_shape_converter = ClientShapeToResourceShapeConverter()
3743
self._resource_shape_to_client_shape_converter = ResourceShapeToClientShapeConverter()
3844

3945

4046
def Table(self, name, **kwargs):
4147
print("EncryptedResource making EncryptedTable")
42-
table_kwargs = dict(
48+
49+
return EncryptedTable(
4350
table=self._resource.Table(name),
4451
encryption_config=self._encryption_config
4552
)
46-
47-
return EncryptedTable(**table_kwargs)
4853

4954
def __getattr__(self, name):
5055
print("calling EncryptedResource.__getattr__")
@@ -111,3 +116,9 @@ def batch_write_item(self, **kwargs):
111116
response = self._copy_missing_sdk_output_fields_to_response(sdk_output, response, output_transform_output)
112117

113118
return response
119+
120+
def _copy_missing_sdk_output_fields_to_response(self, sdk_output, response, output_transform_output):
121+
for sdk_output_key, sdk_output_value in sdk_output.items():
122+
if sdk_output_key not in output_transform_output:
123+
response[sdk_output_key] = sdk_output_value
124+
return response

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def get_item(self, **kwargs):
9191

9292
return response
9393

94+
# TODO refactor across table/client/resource/more?
9495
def _copy_missing_sdk_output_fields_to_response(self, sdk_output, response, output_transform_output):
9596
for sdk_output_key, sdk_output_value in sdk_output.items():
9697
if sdk_output_key not in output_transform_output:
@@ -171,3 +172,16 @@ def query(self, **kwargs):
171172
response = self._copy_missing_sdk_output_fields_to_response(sdk_output, response, output_transform_output)
172173

173174
return response
175+
176+
def __getattr__(self, name):
177+
# Before calling __getattr__, the class will look at its own methods.
178+
# Any methods defined on the class are called before getting to this point.
179+
180+
# __getattr__ doesn't find a class' protected methods by default.
181+
# if name in self._get_protected_methods():
182+
# return getattr(self, name)
183+
# If the class doesn't override a boto3 method, defer to boto3 now.
184+
if hasattr(self._resource, name):
185+
return getattr(self._resource, name)
186+
else:
187+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,24 @@ def attribute_value(self, attribute_value):
1111
return list(ddb_to_dict({"throwaway_key": attribute_value}).values())[0]
1212

1313
def put_item_request(self, put_item_request):
14-
del put_item_request["TableName"]
15-
return super().put_item_request(put_item_request)
14+
out = super().put_item_request(put_item_request)
15+
del out["TableName"]
16+
return out
1617

1718
def get_item_request(self, get_item_request):
18-
del get_item_request["TableName"]
19-
return super().get_item_request(get_item_request)
19+
out = super().get_item_request(get_item_request)
20+
del out["TableName"]
21+
return out
2022

2123
def query_request(self, query_request):
22-
del query_request["TableName"]
23-
return super().query_request(query_request)
24+
out = super().query_request(query_request)
25+
del out["TableName"]
26+
return out
2427

2528
def scan_request(self, scan_request):
26-
del scan_request["TableName"]
27-
return super().scan_request(scan_request)
29+
out = super().scan_request(scan_request)
30+
del out["TableName"]
31+
return out
2832

2933
def expression(self, condition_expression):
3034
# Tables accept the same format as the client (string).
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[{"Ten":{"B":"Af8s6YdAFS4utVKOm4W2YVK6xib1n7TDtOY="},"Two":{"B":"AAKCSt2X5SOJ8zQqkTv9PnOrboo="},"Five":{"B":"AARwceKk16c6ZsWm9bflwhvExQ=="},"Four":{"B":"AAB3VgdPtr3dspOXnWQnSwH9"},"One":{"B":"AAHe3uxIVXy2N2LNs1frVSK2GfPyGn7AJgrE"},"Six":{"L":[{"S":"one"},{"S":"two"},{"S":"three"},{"L":[{"N":"1"},{"N":"2"},{"N":"3"},{"N":"4"}]}]},"Nine":{"NS":["1.2","5.6","3.4"]},"Seven":{"B":"AgBoRAjKSyXqGOpEb+3CcrYSTsTVwJ0DQQ7j7P5mfW07Efuy1c3uyyaZAbTunvJkFIemwX1b9ZLNU5ZrHOKkiV0CFvhdulpRTh/XIT8gb4J9JALC4GfiJqpKEB7oS68a6CuhhEpx7QetphSz+jnp/WjeHFBhWA=="},"Three":{"B":"//8LRASLaVduZ51FWhVvp60FffkOeByvehZrbFhXycPCaPdg"},"Eight":{"B":"AQHZRzJ46nbMoh327WCHP+xGPIxhwsu09VO5Xbq3AYhBZnCjNqcDi9z3iK3g"},"RecNum":{"N":"1"},"aws_dbe_head":{"B":"AQEEhrXqfrq0dNolzOmoh0EYWg2+TY0GIziF9chaDoHu4QALZXNlZWVlc2VlZXMAAQAVYXdzLWNyeXB0by1wdWJsaWMta2V5AERBNU9ZcjZQRExXNkVDM29lSEY1ZjFLSUZnd2RPY2ZMSWNCSXplRG0rMEFmbHQ5M0xGdzZqTjRvN2xnWVhsN2dnQ1E9PQEAEWF3cy1rbXMtaGllcmFyY2h5ACRiZDM4NDJmZi0zMDc2LTQwOTItOTkxOC00Mzk1NzMwMDUwYjgAjO2oPUgfwh2yXVHJoe8MyeiB8TrIIyY0DEJUgPJH8v+30yQTjYGDZY5DlsDB5f/cIZ/FDTEHpoqOlTG7RS0jiOwfAL8lMZ/duOjbvw/pzhij7bVCcp+GHKy3mX/2oVDnn4f1Db4J+yA4/DfrFspT/kY4pHKHgDoDXsa2SWMmmY3lIh6X6RtNvEOgy0U+8+Pu0wWfLpaKHas2+afpIBRYn8HHCu55wjrXLHg9uaM="},"aws_dbe_foot":{"B":"2dk32XzvUvDXvjC5Lxb4pHbGPKeMOO1r6O/xCpDho/6rlx+SKfU6B5h9xz4UeMQxMGUCMQDRBcftyiIcbmsIg31PoBOKgbNrmbsZSls7wlELnMuxT2yzCySoHJ3ruR4XE6kAxeICMHuoSL0XS6YHXqQgxU6Ep+3H35eqmos6XquZJV6tfxd1OTfc34+AgAi53/7mOhEAyw=="}}]
1+
[{"aws_dbe_foot":{"B":"gtTab9XdZyS1Kd0AQhWHXFsm9cgg91RW8ld63mxTzFY0ZAxmXOqbPdZoo6yL5pwFMGUCMQCJhqmWeNBfhK8l0mkU61jmeVXgUdN2wVkhmRCznlkmcMiWozGkJvhbWoAheGWZg98CMEJwv0c02PXl79jIKqFDHrZRpPSLU9y0NdjF6Jj0MX/jVAY99Zwy7KEkLzgBI8wMYQ=="},"Nine":{"NS":["1.2","5.6","3.4"]},"Two":{"B":"AAIsSl4KZCLJ7E0Vj1SQ1Mv/Fwg="},"RecNum":{"N":"1"},"aws_dbe_head":{"B":"AQFcl0of5rcj4IsSF9lhUGLRM2a06GWjxXE9lu8ffpS6bgALZXNlZWVlc2VlZXMAAQAVYXdzLWNyeXB0by1wdWJsaWMta2V5AERBMVowdkhUbExoNGNxZFRNUVE5VUVsWGdVWlh2NjBuTjVSbzZJeUNmUkdZVUVGeVJQZ0ZKTCtJTnhzSzhQdnFsYmc9PQEAEWF3cy1rbXMtaGllcmFyY2h5ACRiZDM4NDJmZi0zMDc2LTQwOTItOTkxOC00Mzk1NzMwMDUwYjgAjJl5CbZ1hVgH9PI06YY2pReku02bIlNoMbkNBmnOmC3SMrWIebUHR/S+qnRCC6Nyu82hbS5SCHdyDsKrCH26vS2tC3qzQEy9dn6w9g7pzhij7bVCcp+GHKy3mX/2T6UyppkAg+0bORfulYoZbR3dagPU7xcqEbv4CnTXYiEzshd5jjYgg94VW4J9E4xsIEzW+rs7dO2goPbzAitUgmAXvemEW3YZwU2igtdr5tg="},"Six":{"L":[{"S":"one"},{"S":"two"},{"S":"three"},{"L":[{"N":"1"},{"N":"2"},{"N":"3"},{"N":"4"}]}]},"Seven":{"B":"AgBRIBzYUjm2aX2XMI2hC3iOoTs0HvrpReBLOMYxxhTYoxZo82cHgoo9MJgAGEzW59ZP8GLgvM2SxsHh2GcwFlk4vYzwuB2UqkuI7VuipQskde8+cxc08+nRMmdh1gNauujm8Yp/zR0BMwfeJzthKgBT8K7ehw=="},"Eight":{"B":"AQGXvhziLvrbnFcEvcGoyURaVGPizJ7DOGt5tvEY3wbihoJN1D4YISTlZSlY"},"One":{"B":"AAFCkBUdc1t4JcvDMa8lSwUtOF7kJTCeXyGW"},"Three":{"B":"//+c/58Y0m6S653ePlqiFB2Wx4dLw7ys+NGoSOFkx3/dMJyV"},"Four":{"B":"AAA0FKipvCQdQ1I/mYcHnOiw"},"Five":{"B":"AARkzd9SiUHCOWke42PPWC6Y7g=="},"Ten":{"B":"Af/bd5gH2gIIlhiSDutakSxbzM1MueKUELM="}}]

TestVectors/runtimes/python/src/dbesdk_ddb_test_vectors/internaldafny/extern/CreateInterceptedDDBResource.py

Lines changed: 70 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
dict_to_ddb,
1313
ddb_to_dict,
1414
)
15+
from aws_database_encryption_sdk.internal.resource_to_client import ResourceShapeToClientShapeConverter
16+
from aws_database_encryption_sdk.internal.client_to_resource import ClientShapeToResourceShapeConverter
1517

1618
# TestVectors-only override of ._flush method:
1719
# persist response in self._response for TestVectors output processing.
@@ -49,123 +51,87 @@ class DynamoDBClientWrapperForDynamoDBResource:
4951
def __init__(self, resource, client):
5052
self._resource = resource
5153
self._client = client
52-
53-
def put_item(self, **kwargs):
54-
pass
55-
56-
def get_item(self, **kwargs):
57-
key_dynamodb_format = kwargs["Key"]
58-
key_dict_format = ddb_to_dict(key_dynamodb_format)
59-
kwargs["Key"] = key_dict_format
60-
dict_format_output = self._resource.get_item(**kwargs)
61-
dynamodb_format_item = dict_format_output
62-
if "Item" in dict_format_output:
63-
dynamodb_format_item["Item"] = dict_to_ddb(dict_format_output["Item"])
64-
return dynamodb_format_item
65-
66-
def _convert_batch_write_item_request_from_dynamo_to_dict(self, **kwargs):
67-
for table, requests in kwargs["RequestItems"].items():
68-
dict_requests = []
69-
for request in requests:
70-
request_name_list = list(request.keys())
71-
if len(request_name_list) > 1:
72-
raise ValueError("Cannot send more than one request in a single request")
73-
request_name = request_name_list[0]
74-
if request_name == "PutRequest":
75-
dict_request = ddb_to_dict(request[request_name]["Item"])
76-
dict_requests.append({request_name: {"Item": dict_request}})
77-
elif request_name == "DeleteRequest":
78-
dict_request = ddb_to_dict(request[request_name]["Key"])
79-
dict_requests.append({request_name: {"Key": dict_request}})
80-
else:
81-
raise ValueError(f"Unknown batch_write_items method key: {request_name}")
82-
kwargs["RequestItems"][table] = dict_requests
83-
return kwargs
54+
self._client_shape_to_resource_shape_converter = ClientShapeToResourceShapeConverter()
55+
self._resource_shape_to_client_shape_converter = ResourceShapeToClientShapeConverter()
8456

8557
def batch_write_item(self, **kwargs):
86-
print(f"batch_write_item {kwargs=}")
87-
dict_formatted = self._convert_batch_write_item_request_from_dynamo_to_dict(**kwargs)
88-
return self._resource.batch_write_item(**dict_formatted)
89-
90-
# # print(f"batch_write_item {kwargs=}")
91-
# # Parse boto3 client.batch_write_item input into table.batch_writer() calls
92-
# # client.batch_write_item: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/batch_write_item.html
93-
# # table.batch_writer(): https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/batch_writer.html
94-
# tables = kwargs["RequestItems"]
95-
# table_names = list(tables.keys())
96-
# if len(tables.keys()) > 1:
97-
# raise ValueError("Table batch_write_item only supports 1 table in a request.")
98-
# requests = tables[table_names[0]]
99-
# with self._table.batch_writer() as batch:
100-
101-
# batch._flush=types.MethodType(_flush_and_persist_response, batch)
102-
103-
# for request in requests:
104-
# request_name_list = list(request.keys())
105-
# if len(request_name_list) > 1:
106-
# raise ValueError("Cannot send more than one request in a single request")
107-
# request_name = request_name_list[0]
108-
# if request_name == "PutRequest":
109-
# dict_request = ddb_to_dict(request[request_name]["Item"])
110-
# boto3_request = {"Item": dict_request}
111-
# batch.put_item(**boto3_request)
112-
# # dynamodb_request = request[request_name]
113-
# # print(f"{dynamodb_request}")
114-
# # batch.put_item(**dynamodb_request)
115-
# elif request_name == "DeleteRequest":
116-
# batch.delete_item(Key=ddb_to_dict(request["Key"]))
117-
# else:
118-
# raise ValueError(f"Unknown batch_write_items method key: {request_name}")
119-
# return batch._response
120-
58+
# The input here is from the DBESDK TestVectors, which is in the shape of a client request.
59+
# Convert the client request to a resource request to be passed to the table.
60+
table_input = self._client_shape_to_resource_shape_converter.batch_write_item_request(kwargs)
61+
# ClientShapeToResourceShapeConverters don't convert DynamoDB expressions from strings to Condition objects,
62+
# because resource shapes can accept strings.
63+
# For TestVectors coverage, use a TestVectors-internal string-to-Condition converter.
64+
# TODO: Use a shared Condition converter.
65+
table_output = self._resource.batch_write_item(**table_input)
66+
client_output = self._resource_shape_to_client_shape_converter.batch_write_item_response(table_output)
67+
return client_output
68+
12169
def batch_get_item(self, **kwargs):
122-
# batch_get_item doesn't exist on boto3 tables.
123-
# For TestVector compatibility, just make a series of get_item calls,
124-
# and massage inputs/outputs into expected formats.
125-
tables = kwargs["RequestItems"]
126-
table_names = list(tables.keys())
127-
if len(tables.keys()) > 1:
128-
raise ValueError("Table batch_get_item only supports 1 table in a request.")
129-
requests = tables[table_names[0]]
130-
with self._table.batch_writer() as batch:
131-
132-
batch._flush=types.MethodType(_flush_and_persist_response, batch)
133-
134-
for request in requests:
135-
request_name_list = list(request.keys())
136-
if len(request_name_list) > 1:
137-
raise ValueError("Cannot send more than one request in a single request")
138-
request_name = request_name_list[0]
139-
if request_name == "PutRequest":
140-
dict_request = ddb_to_dict(request[request_name]["Item"])
141-
boto3_request = {"Item": dict_request}
142-
batch.put_item(**boto3_request)
143-
# dynamodb_request = request[request_name]
144-
# print(f"{dynamodb_request}")
145-
# batch.put_item(**dynamodb_request)
146-
elif request_name == "DeleteRequest":
147-
batch.delete_item(Key=ddb_to_dict(request["Key"]))
148-
else:
149-
raise ValueError(f"Unknown batch_write_items method key: {request_name}")
150-
return batch._response
151-
152-
70+
table_input = self._client_shape_to_resource_shape_converter.batch_get_item_request(kwargs)
71+
table_output = self._resource.batch_get_item(**table_input)
72+
client_output = self._resource_shape_to_client_shape_converter.batch_get_item_response(table_output)
73+
return client_output
74+
15375
def scan(self, **kwargs):
154-
pass
76+
# Resources don't have scan, but our EncryptedResources can provide EncryptedTables that do support scan.
77+
# This path tests that the EncryptedTables provided by EncryptedResources can used for scan.
78+
table_name = kwargs["TableName"]
79+
table_input = self._client_shape_to_resource_shape_converter.scan_request(kwargs)
80+
encrypted_table = self._resource.Table(table_name)
81+
table_output = encrypted_table.scan(**table_input)
82+
table_shape_converter = ResourceShapeToClientShapeConverter(table_name=table_name)
83+
client_output = table_shape_converter.scan_response(table_output)
84+
return client_output
85+
86+
def put_item(self, **kwargs):
87+
# Resources don't have put_item, but our EncryptedResources can provide EncryptedTables that do support put_item.
88+
# This path tests that the EncryptedTables provided by EncryptedResources can used for put_item.
89+
table_name = kwargs["TableName"]
90+
table_input = self._client_shape_to_resource_shape_converter.put_item_request(kwargs)
91+
encrypted_table = self._resource.Table(table_name)
92+
table_output = encrypted_table.put_item(**table_input)
93+
table_shape_converter = ResourceShapeToClientShapeConverter(table_name=table_name)
94+
client_output = table_shape_converter.put_item_response(table_output)
95+
return client_output
96+
97+
def get_item(self, **kwargs):
98+
# Resources don't have get_item, but our EncryptedResources can provide EncryptedTables that do support get_item.
99+
# This path tests that the EncryptedTables provided by EncryptedResources can used for get_item.
100+
table_name = kwargs["TableName"]
101+
table_input = self._client_shape_to_resource_shape_converter.get_item_request(kwargs)
102+
encrypted_table = self._resource.Table(table_name)
103+
table_output = encrypted_table.get_item(**table_input)
104+
table_shape_converter = ResourceShapeToClientShapeConverter(table_name=table_name)
105+
client_output = table_shape_converter.get_item_response(table_output)
106+
return client_output
107+
108+
def query(self, **kwargs):
109+
# Resources don't have query, but our EncryptedResources can provide EncryptedTables that do support query.
110+
# This path tests that the EncryptedTables provided by EncryptedResources can used for query.
111+
table_name = kwargs["TableName"]
112+
table_input = self._client_shape_to_resource_shape_converter.put_item_request(kwargs)
113+
encrypted_table = self._resource.Table(table_name)
114+
table_output = encrypted_table.query(**table_input)
115+
table_shape_converter = ResourceShapeToClientShapeConverter(table_name=table_name)
116+
client_output = table_shape_converter.put_item_response(table_output)
117+
return client_output
155118

156119
def transact_get_items(self, **kwargs):
157-
pass
120+
raise NotImplementedError("transact_get_items not supported on resources")
158121

159122
def transact_write_items(self, **kwargs):
160-
pass
161-
162-
def query(self, **kwargs):
163-
pass
123+
raise NotImplementedError("transact_get_items not supported on resources")
164124

165125
def delete_table(self, **kwargs):
126+
# Resources don't have delete_table. Plus, DBESDK doesn't intercept DeleteTable calls.
127+
# TestVectors only use this to ensure a new, clean table is created for each test.
128+
# Defer to the underlying boto3 client to delete the table.
166129
return self._client.delete_table(**kwargs)
167130

168131
def create_table(self, **kwargs):
132+
# Resources don't have create_table. Plus, DBESDK doesn't intercept CreateTable calls.
133+
# TestVectors only use this to ensure a new, clean table is created for each test.
134+
# Defer to the underlying boto3 client to create a table.
169135
return self._client.create_table(**kwargs)
170136

171137

0 commit comments

Comments
 (0)