3
3
"""High-level helper class to provide an encrypting wrapper for boto3 DynamoDB clients."""
4
4
import botocore .client
5
5
from typing import Optional , Any , Dict , List
6
+ from copy import deepcopy
6
7
7
8
from aws_database_encryption_sdk .smithygenerated .aws_cryptography_dbencryptionsdk_dynamodb_transforms .models import (
8
9
GetItemInputTransformInput ,
22
23
QueryInputTransformInput ,
23
24
QueryOutputTransformInput ,
24
25
)
25
- from aws_database_encryption_sdk .transform import (
26
- dict_to_ddb ,
27
- ddb_to_dict ,
28
- list_of_ddb_to_list_of_dict ,
29
- list_of_dict_to_list_of_ddb ,
30
- )
31
26
from aws_database_encryption_sdk .smithygenerated .aws_cryptography_dbencryptionsdk_dynamodb_transforms .client import (
32
27
DynamoDbEncryptionTransforms
33
28
)
37
32
from aws_database_encryption_sdk .encrypted .paginator import EncryptedPaginator
38
33
from aws_database_encryption_sdk .internal .resource_to_client import ResourceShapeToClientShapeConverter
39
34
from aws_database_encryption_sdk .internal .client_to_resource import ClientShapeToResourceShapeConverter
40
- from aws_database_encryption_sdk .internal . utils import _copy_sdk_response_to_dbesdk_response
35
+ from aws_database_encryption_sdk .encrypted . boto3_interface import EncryptedBotoInterface
41
36
42
- class EncryptedClient :
37
+ class EncryptedClient ( EncryptedBotoInterface ) :
43
38
"""Wrapper for a boto3 DynamoDB client that transparently encrypts/decrypts items.
44
39
45
40
This class implements the complete boto3 DynamoDB client API, allowing it to serve as a
@@ -84,8 +79,8 @@ def __init__(
84
79
expect_standard_dictionaries (Optional[bool]): Does the underlying boto3 client expect items to be standard Python
85
80
dictionaries? This should only be set to True if you are using a client obtained
86
81
from a service resource or table resource (ex: `table.meta.client`).
87
- If this is True, the client will expect item-like shapes to be standard Python dictionaries (default: False).
88
-
82
+ If this is True, EncryptedClient will expect item-like shapes to be
83
+ standard Python dictionaries (default: False).
89
84
"""
90
85
self ._client = client
91
86
self ._encryption_config = encryption_config
@@ -94,7 +89,6 @@ def __init__(
94
89
)
95
90
self ._expect_standard_dictionaries = expect_standard_dictionaries
96
91
self ._resource_to_client_shape_converter = ResourceShapeToClientShapeConverter ()
97
- #
98
92
self ._client_to_resource_shape_converter = ClientShapeToResourceShapeConverter (delete_table_name = False )
99
93
100
94
def put_item (self , ** kwargs ) -> Dict [str , Any ]:
@@ -402,10 +396,10 @@ def _client_operation_logic(
402
396
dict: The transformed response from DynamoDB
403
397
"""
404
398
# Transform input from Python dictionary JSON to DynamoDB JSON if required
405
- sdk_input = operation_input . copy ( )
399
+ sdk_input = deepcopy ( operation_input )
406
400
if self ._expect_standard_dictionaries :
407
- if "TableName" in operation_input :
408
- self ._resource_to_client_shape_converter .table_name = operation_input ["TableName" ]
401
+ if "TableName" in sdk_input :
402
+ self ._resource_to_client_shape_converter .table_name = sdk_input ["TableName" ]
409
403
sdk_input = input_item_to_ddb_transform_method (sdk_input )
410
404
411
405
# Apply encryption transformation to the user-supplied input
@@ -433,26 +427,18 @@ def _client_operation_logic(
433
427
).transformed_output
434
428
435
429
# Copy any missing fields from the SDK output to the response
436
- dbesdk_response = _copy_sdk_response_to_dbesdk_response (sdk_response , dbesdk_response )
430
+ dbesdk_response = self . _copy_sdk_response_to_dbesdk_response (sdk_response , dbesdk_response )
437
431
438
432
if self ._expect_standard_dictionaries :
439
433
dbesdk_response = output_item_to_dict_transform_method (dbesdk_response )
440
434
441
435
return dbesdk_response
442
436
443
- def __getattr__ (self , name : str ) -> Any :
444
- """Delegate unknown attributes to the underlying client.
445
-
446
- Args:
447
- name: The name of the attribute to get
448
-
437
+ @property
438
+ def _boto_client_attr_name (self ) -> str :
439
+ """Name of the attribute containing the underlying boto3 client.
440
+
449
441
Returns:
450
- Any: The attribute value from the underlying client
451
-
452
- Raises:
453
- AttributeError: If the attribute doesn't exist on the underlying client
442
+ str: '_client'
454
443
"""
455
- if hasattr (self ._client , name ):
456
- return getattr (self ._client , name )
457
- else :
458
- raise AttributeError (f"'{ self .__class__ .__name__ } ' object has no attribute '{ name } '" )
444
+ return '_client'
0 commit comments