Skip to content

Commit 381538b

Browse files
author
Lucas McDonald
committed
sync
1 parent 9c38d2b commit 381538b

File tree

3 files changed

+258
-12
lines changed

3 files changed

+258
-12
lines changed

DynamoDbEncryption/runtimes/python/src/aws_dbesdk_dynamodb/encrypted/client.py

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,18 @@
1717
DynamoDbEncryptionTransforms,
1818
)
1919
from aws_dbesdk_dynamodb.smithygenerated.aws_cryptography_dbencryptionsdk_dynamodb_transforms.models import (
20+
BatchExecuteStatementInputTransformInput,
21+
BatchExecuteStatementOutputTransformInput,
2022
BatchGetItemInputTransformInput,
2123
BatchGetItemOutputTransformInput,
2224
BatchWriteItemInputTransformInput,
2325
BatchWriteItemOutputTransformInput,
26+
DeleteItemInputTransformInput,
27+
DeleteItemOutputTransformInput,
28+
ExecuteStatementInputTransformInput,
29+
ExecuteStatementOutputTransformInput,
30+
ExecuteTransactionInputTransformInput,
31+
ExecuteTransactionOutputTransformInput,
2432
GetItemInputTransformInput,
2533
GetItemOutputTransformInput,
2634
PutItemInputTransformInput,
@@ -33,6 +41,8 @@
3341
TransactGetItemsOutputTransformInput,
3442
TransactWriteItemsInputTransformInput,
3543
TransactWriteItemsOutputTransformInput,
44+
UpdateItemInputTransformInput,
45+
UpdateItemOutputTransformInput,
3646
)
3747

3848

@@ -57,8 +67,14 @@ class EncryptedClient(EncryptedBotoInterface):
5767
* ``batch_get_item``
5868
* ``transact_get_items``
5969
* ``transact_write_items``
70+
* ``delete_item``
6071
61-
The ``update_item`` operation is not currently supported. Calling this operation will raise ``NotImplementedError``.
72+
The following operations are not supported and will raise DynamoDbEncryptionTransformsException:
73+
74+
* ``execute_statement``
75+
* ``execute_transaction``
76+
* ``batch_execute_statement``
77+
* ``update_item``
6278
6379
Any other operations on this class will defer to the underlying boto3 DynamoDB client's implementation.
6480
@@ -337,18 +353,131 @@ def transact_write_items(self, **kwargs) -> dict[str, Any]:
337353
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.transact_write_items_response,
338354
)
339355

356+
def delete_item(self, **kwargs):
357+
"""
358+
Delete an item from a table by the specified key.
359+
360+
The input and output syntaxes match those for the boto3 DynamoDB client ``delete_item`` API:
361+
362+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/client/delete_item.html
363+
364+
Args:
365+
**kwargs: Keyword arguments to pass to the operation. This matches the boto3 client ``delete_item``
366+
request syntax.
367+
368+
Returns:
369+
dict: The response from DynamoDB. This matches the boto3 client ``delete_item`` response syntax.
370+
Any values in the ``"Attributes"`` field will be decrypted locally after being read from DynamoDB.
371+
372+
"""
373+
return self._client_operation_logic(
374+
operation_input=kwargs,
375+
input_item_to_ddb_transform_method=self._resource_to_client_shape_converter.delete_item_request,
376+
input_item_to_dict_transform_method=self._client_to_resource_shape_converter.delete_item_request,
377+
input_transform_method=self._transformer.delete_item_input_transform,
378+
input_transform_shape=DeleteItemInputTransformInput,
379+
output_transform_method=self._transformer.delete_item_output_transform,
380+
output_transform_shape=DeleteItemOutputTransformInput,
381+
client_method=self._client.delete_item,
382+
output_item_to_dict_transform_method=self._client_to_resource_shape_converter.delete_item_response,
383+
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.delete_item_response,
384+
)
385+
386+
def execute_statement(self, **kwargs):
387+
"""
388+
Not implemented. Raises DynamoDbEncryptionTransformsException.
389+
390+
Args:
391+
**kwargs: Any arguments passed to this method
392+
393+
Raises:
394+
DynamoDbEncryptionTransformsException: This operation is not supported on encrypted tables.
395+
396+
"""
397+
return self._client_operation_logic(
398+
operation_input=kwargs,
399+
input_item_to_ddb_transform_method=self._resource_to_client_shape_converter.execute_statement_request,
400+
input_item_to_dict_transform_method=self._client_to_resource_shape_converter.execute_statement_request,
401+
input_transform_method=self._transformer.execute_statement_input_transform,
402+
input_transform_shape=ExecuteStatementInputTransformInput,
403+
output_transform_method=self._transformer.execute_statement_output_transform,
404+
output_transform_shape=ExecuteStatementOutputTransformInput,
405+
client_method=self._client.execute_statement,
406+
output_item_to_dict_transform_method=self._client_to_resource_shape_converter.execute_statement_response,
407+
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.execute_statement_response,
408+
)
409+
410+
def execute_transaction(self, **kwargs):
411+
"""
412+
Not implemented. Raises DynamoDbEncryptionTransformsException.
413+
414+
Args:
415+
**kwargs: Any arguments passed to this method
416+
417+
Raises:
418+
DynamoDbEncryptionTransformsException: This operation is not supported on encrypted tables.
419+
420+
"""
421+
return self._client_operation_logic(
422+
operation_input=kwargs,
423+
input_item_to_ddb_transform_method=self._resource_to_client_shape_converter.execute_transaction_request,
424+
input_item_to_dict_transform_method=self._client_to_resource_shape_converter.execute_transaction_request,
425+
input_transform_method=self._transformer.execute_transaction_input_transform,
426+
input_transform_shape=ExecuteTransactionInputTransformInput,
427+
output_transform_method=self._transformer.execute_transaction_output_transform,
428+
output_transform_shape=ExecuteTransactionOutputTransformInput,
429+
client_method=self._client.execute_transaction,
430+
output_item_to_dict_transform_method=self._client_to_resource_shape_converter.execute_transaction_response,
431+
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.execute_transaction_response,
432+
)
433+
340434
def update_item(self, **kwargs):
341435
"""
342-
Not implemented. Raises NotImplementedError.
436+
Not implemented. Raises DynamoDbEncryptionTransformsException.
343437
344438
Args:
345439
**kwargs: Any arguments passed to this method
346440
347441
Raises:
348-
NotImplementedError: This operation is not yet implemented
442+
DynamoDbEncryptionTransformsException: This operation is not supported on encrypted tables.
349443
350444
"""
351-
raise NotImplementedError('"update_item" is not yet implemented')
445+
return self._client_operation_logic(
446+
operation_input=kwargs,
447+
input_item_to_ddb_transform_method=self._resource_to_client_shape_converter.update_item_request,
448+
input_item_to_dict_transform_method=self._client_to_resource_shape_converter.update_item_request,
449+
input_transform_method=self._transformer.update_item_input_transform,
450+
input_transform_shape=UpdateItemInputTransformInput,
451+
output_transform_method=self._transformer.update_item_output_transform,
452+
output_transform_shape=UpdateItemOutputTransformInput,
453+
client_method=self._client.update_item,
454+
output_item_to_dict_transform_method=self._client_to_resource_shape_converter.update_item_response,
455+
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.update_item_response,
456+
)
457+
458+
def batch_execute_statement(self, **kwargs):
459+
"""
460+
Not implemented. Raises DynamoDbEncryptionTransformsException.
461+
462+
Args:
463+
**kwargs: Any arguments passed to this method
464+
465+
Raises:
466+
DynamoDbEncryptionTransformsException: This operation is not supported on encrypted tables.
467+
468+
"""
469+
return self._client_operation_logic(
470+
operation_input=kwargs,
471+
input_item_to_ddb_transform_method=self._resource_to_client_shape_converter.batch_execute_statement_request,
472+
input_item_to_dict_transform_method=self._client_to_resource_shape_converter.batch_execute_statement_request,
473+
input_transform_method=self._transformer.batch_execute_statement_input_transform,
474+
input_transform_shape=BatchExecuteStatementInputTransformInput,
475+
output_transform_method=self._transformer.batch_execute_statement_output_transform,
476+
output_transform_shape=BatchExecuteStatementOutputTransformInput,
477+
client_method=self._client.batch_execute_statement,
478+
output_item_to_dict_transform_method=self._client_to_resource_shape_converter.batch_execute_statement_response,
479+
output_item_to_ddb_transform_method=self._resource_to_client_shape_converter.batch_execute_statement_response,
480+
)
352481

353482
def get_paginator(self, operation_name: str) -> EncryptedPaginator | botocore.client.Paginator:
354483
"""

DynamoDbEncryption/runtimes/python/test/integ/encrypted/test_client.py

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
from aws_dbesdk_dynamodb.encrypted.client import EncryptedClient
77
from aws_dbesdk_dynamodb.encrypted.paginator import EncryptedPaginator
8+
from aws_dbesdk_dynamodb.smithygenerated.aws_cryptography_dbencryptionsdk_dynamodb_transforms.errors import (
9+
DynamoDbEncryptionTransformsException,
10+
)
811

912
from ...constants import (
1013
INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
@@ -21,12 +24,20 @@
2124
simple_key_dict,
2225
)
2326
from ...requests import (
27+
basic_batch_execute_statement_request_ddb,
28+
basic_batch_execute_statement_request_dict,
2429
basic_batch_get_item_request_ddb,
2530
basic_batch_get_item_request_dict,
2631
basic_batch_write_item_delete_request_ddb,
2732
basic_batch_write_item_delete_request_dict,
2833
basic_batch_write_item_put_request_ddb,
2934
basic_batch_write_item_put_request_dict,
35+
basic_delete_item_request_ddb,
36+
basic_delete_item_request_dict,
37+
basic_execute_statement_request_ddb,
38+
basic_execute_statement_request_dict,
39+
basic_execute_transaction_request_ddb,
40+
basic_execute_transaction_request_dict,
3041
basic_get_item_request_ddb,
3142
basic_get_item_request_dict,
3243
basic_put_item_request_ddb,
@@ -41,6 +52,8 @@
4152
basic_transact_write_item_delete_request_dict,
4253
basic_transact_write_item_put_request_ddb,
4354
basic_transact_write_item_put_request_dict,
55+
basic_update_item_request_ddb,
56+
basic_update_item_request_dict,
4457
)
4558
from . import sort_dynamodb_json_lists
4659

@@ -154,8 +167,15 @@ def get_item_request(expect_standard_dictionaries, test_item):
154167
return basic_get_item_request_ddb(test_item)
155168

156169

157-
def test_GIVEN_valid_put_and_get_requests_WHEN_put_and_get_THEN_round_trip_passes(
158-
client, put_item_request, get_item_request
170+
@pytest.fixture
171+
def delete_item_request(expect_standard_dictionaries, test_item):
172+
if expect_standard_dictionaries:
173+
return {**basic_delete_item_request_dict(test_item), "TableName": INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME}
174+
return basic_delete_item_request_ddb(test_item)
175+
176+
177+
def test_GIVEN_valid_put_and_get_and_delete_requests_WHEN_put_and_get_and_delete_THEN_round_trip_passes(
178+
client, put_item_request, get_item_request, delete_item_request
159179
):
160180
# Given: Valid put_item request
161181
# When: put_item
@@ -174,6 +194,23 @@ def test_GIVEN_valid_put_and_get_requests_WHEN_put_and_get_THEN_round_trip_passe
174194
actual_item = sort_dynamodb_json_lists(get_response["Item"])
175195
assert expected_item == actual_item
176196

197+
# Given: Valid delete_item request for the same item
198+
# When: delete_item
199+
delete_response = client.delete_item(**{**delete_item_request, "ReturnValues": "ALL_OLD"})
200+
# Then: delete_item succeeds and contains the expected response
201+
assert delete_response["ResponseMetadata"]["HTTPStatusCode"] == 200
202+
# DynamoDB JSON uses lists to represent sets, so strict equality can fail.
203+
# Sort lists to ensure consistent ordering when comparing expected and actual items.
204+
expected_item = sort_dynamodb_json_lists(put_item_request["Item"])
205+
actual_item = sort_dynamodb_json_lists(delete_response["Attributes"])
206+
assert expected_item == actual_item
207+
208+
# Given: Valid get_item request for the same item
209+
# When: get_item
210+
get_response = client.get_item(**get_item_request)
211+
# Then: get_item is empty (i.e. the item was deleted)
212+
assert "Item" not in get_response
213+
177214

178215
@pytest.fixture
179216
def batch_write_item_put_request(expect_standard_dictionaries, multiple_test_items):
@@ -346,18 +383,89 @@ def test_GIVEN_valid_transact_write_and_get_requests_WHEN_transact_write_and_get
346383
assert transact_write_delete_response["ResponseMetadata"]["HTTPStatusCode"] == 200
347384

348385

349-
def test_WHEN_update_item_THEN_raises_not_implemented_error():
350-
"""Test that update_item raises NotImplementedError."""
386+
@pytest.fixture
387+
def update_item_request(expect_standard_dictionaries, test_item):
388+
if expect_standard_dictionaries:
389+
return basic_update_item_request_dict(test_item)
390+
return basic_update_item_request_ddb(test_item)
391+
392+
393+
def test_WHEN_update_item_THEN_raises_DynamoDbEncryptionTransformsException():
394+
"""Test that update_item raises DynamoDbEncryptionTransformsException."""
351395
# Given: Encrypted client and update item parameters
352-
# When: Calling update_item
353-
with pytest.raises(NotImplementedError):
396+
# Then: DynamoDbEncryptionTransformsException is raised
397+
with pytest.raises(DynamoDbEncryptionTransformsException):
398+
# When: Calling update_item
354399
encrypted_client(expect_standard_dictionaries=False).update_item(
355400
TableName=INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
356-
Key={"partition_key": "test-key", "sort_key": 1},
401+
Key={"partition_key": {"S": "test-key"}, "sort_key": {"N": "1"}},
357402
UpdateExpression="SET attribute1 = :val",
358403
ExpressionAttributeValues={":val": {"S": "new value"}},
359404
)
360-
# Then: NotImplementedError is raised
405+
406+
407+
@pytest.fixture
408+
def execute_statement_request(expect_standard_dictionaries, test_item):
409+
if expect_standard_dictionaries:
410+
return basic_execute_statement_request_dict(test_item)
411+
return basic_execute_statement_request_ddb(test_item)
412+
413+
414+
def test_WHEN_execute_statement_THEN_raises_DynamoDbEncryptionTransformsException():
415+
"""Test that execute_statement raises DynamoDbEncryptionTransformsException."""
416+
# Given: Encrypted client and update item parameters
417+
# Then: DynamoDbEncryptionTransformsException is raised
418+
with pytest.raises(DynamoDbEncryptionTransformsException):
419+
# When: Calling update_item
420+
encrypted_client(expect_standard_dictionaries=False).execute_statement(
421+
Statement="SELECT * FROM " + INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
422+
)
423+
424+
425+
@pytest.fixture
426+
def execute_transaction_request(expect_standard_dictionaries, test_item):
427+
if expect_standard_dictionaries:
428+
return basic_execute_transaction_request_dict(test_item)
429+
return basic_execute_transaction_request_ddb(test_item)
430+
431+
432+
def test_WHEN_execute_transaction_THEN_raises_DynamoDbEncryptionTransformsException():
433+
"""Test that execute_transaction raises DynamoDbEncryptionTransformsException."""
434+
# Given: Encrypted client and update item parameters
435+
# Then: DynamoDbEncryptionTransformsException is raised
436+
with pytest.raises(DynamoDbEncryptionTransformsException):
437+
# When: Calling update_item
438+
encrypted_client(expect_standard_dictionaries=False).execute_transaction(
439+
TransactStatements=[
440+
{
441+
"Statement": "SELECT * FROM " + INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
442+
"Parameters": [],
443+
}
444+
],
445+
)
446+
447+
448+
@pytest.fixture
449+
def batch_execute_statement_request(expect_standard_dictionaries, test_item):
450+
if expect_standard_dictionaries:
451+
return basic_batch_execute_statement_request_dict(test_item)
452+
return basic_batch_execute_statement_request_ddb(test_item)
453+
454+
455+
def test_WHEN_batch_execute_statement_THEN_raises_DynamoDbEncryptionTransformsException():
456+
"""Test that batch_execute_statement raises DynamoDbEncryptionTransformsException."""
457+
# Given: Encrypted client and update item parameters
458+
# Then: DynamoDbEncryptionTransformsException is raised
459+
with pytest.raises(DynamoDbEncryptionTransformsException):
460+
# When: Calling update_item
461+
encrypted_client(expect_standard_dictionaries=False).batch_execute_statement(
462+
Statements=[
463+
{
464+
"Statement": "SELECT * FROM " + INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
465+
"Parameters": [],
466+
}
467+
],
468+
)
361469

362470

363471
def test_WHEN_get_paginator_THEN_correct_paginator_is_returned():

Examples/runtimes/python/DynamoDBEncryption/src/basic_put_get_example/with_encrypted_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,12 @@ def encrypted_client_put_get_example(
149149
# Demonstrate that GetItem succeeded and returned the decrypted item
150150
assert get_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
151151
assert get_item_response["Item"] == item_to_encrypt
152+
153+
# 8. Clean up the item we put into the table by deleting it.
154+
delete_item_request = {"TableName": dynamodb_table_name, "Key": key_to_get}
155+
delete_item_response = encrypted_client.delete_item(**delete_item_request)
156+
157+
# Demonstrate that DeleteItem succeeded
158+
assert delete_item_response["ResponseMetadata"]["HTTPStatusCode"] == 200
159+
get_item_response = encrypted_client.get_item(**get_item_request)
160+
assert "Item" not in get_item_response

0 commit comments

Comments
 (0)