boto3-assist 0.18.0__tar.gz → 0.20.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.vscode/settings.json +2 -1
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/PKG-INFO +1 -1
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/pyproject.toml +1 -1
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb.py +31 -11
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +4 -1
- boto3_assist-0.20.0/src/boto3_assist/dynamodb/dynamodb_re_indexer.py +165 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/serialization_utility.py +1 -1
- boto3_assist-0.20.0/src/boto3_assist/version.py +1 -0
- boto3_assist-0.20.0/tests/unit/common/db_test_helpers.py +50 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/user_model.py +9 -9
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/user_required_fields_model.py +0 -1
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py +24 -25
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py +4 -4
- boto3_assist-0.20.0/tests/unit/dynamodb_tests/dynamodb_query_test.py +66 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py +4 -4
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/lambda_tests/event_info_test.py +1 -1
- boto3_assist-0.18.0/src/boto3_assist/version.py +0 -1
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.env.docker +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.env.docker.001 +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.env.docker.nosql.workbench +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.env.unittest +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.gitignore +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.vscode/launch.json +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/.vscode/tasks.json +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/LICENSE-EXPLAINED.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/LICENSE.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/README.md +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/aws_regions_with_status.csv +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/aws_regions_with_status.json +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/devops/build.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/devops/readme.md +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/cloudwatch/log_report.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/models/order_item_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/models/order_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/models/product_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/models/user_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/models/user_post_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/order_example/main.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/order_example/products.json +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/order_item_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/order_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/product_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/table_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/user_post_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/user_service.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/user_post_example/main.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/ec2/regions_report.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/module-headers.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/mypy.ini +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/requirements-dev.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/requirements.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/run-checks.sh +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/run_unit_tests.sh +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/aws_config.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/boto3session.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cognito/user.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/connection_tracker.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/readme.md +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/environment_services/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/http_status_codes.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/models/serializable_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/role_assumption_mixin.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/s3/s3.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/s3/s3_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/s3/s3_object.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/session_setup_mixin.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/ssm/connection.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/file_operations.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/http_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/string_utility.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/integration/cross_account_connection_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/integration/tenant.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/integration/tenant_services.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/aws_config_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/base.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/content_block.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/page.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/template.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/simple_model.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_model_projections_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_model_serializtion_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/examples_test/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/examples_test/user_service_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/lambda_tests/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/models/person.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/models/user.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/serializable_model_person_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/serializable_model_user_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/serializable_model_wide_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/parameter_store/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/parameter_store/parameter_store_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/s3/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/s3/files/test.txt +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/s3/s3_event_data_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/s3/s3_file_delete_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/s3/s3_file_upload_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/session_tests/test_boto3_session_manager.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/utilities/__init__.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/utilities/serialization_utility_test.py +0 -0
- {boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/utilities/string_utility_test.py +0 -0
|
@@ -15,11 +15,11 @@ from boto3.dynamodb.conditions import (
|
|
|
15
15
|
ComparisonCondition,
|
|
16
16
|
ConditionBase,
|
|
17
17
|
)
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from
|
|
21
|
-
from
|
|
22
|
-
|
|
18
|
+
from .dynamodb_connection import DynamoDBConnection
|
|
19
|
+
from .dynamodb_helpers import DynamoDBHelpers
|
|
20
|
+
from .dynamodb_model_base import DynamoDBModelBase
|
|
21
|
+
from ..utilities.string_utility import StringUtility
|
|
22
|
+
from .dynamodb_index import DynamoDBIndex
|
|
23
23
|
|
|
24
24
|
logger = Logger()
|
|
25
25
|
|
|
@@ -69,9 +69,9 @@ class DynamoDB(DynamoDBConnection):
|
|
|
69
69
|
"""
|
|
70
70
|
Save an item to the database
|
|
71
71
|
Args:
|
|
72
|
-
item (dict): DynamoDB
|
|
72
|
+
item (dict): DynamoDB Dictionary Object or DynamoDBModelBase. Supports the "client" or
|
|
73
73
|
"resource" syntax
|
|
74
|
-
table_name (str): The
|
|
74
|
+
table_name (str): The DynamoDb Table Name
|
|
75
75
|
source (str, optional): The source of the call, used for logging. Defaults to None.
|
|
76
76
|
|
|
77
77
|
Raises:
|
|
@@ -98,7 +98,7 @@ class DynamoDB(DynamoDBConnection):
|
|
|
98
98
|
except Exception as e: # pylint: disable=w0718
|
|
99
99
|
logger.exception(e)
|
|
100
100
|
raise RuntimeError(
|
|
101
|
-
"An error
|
|
101
|
+
"An error occurred during model conversion. The entry was not saved. "
|
|
102
102
|
) from e
|
|
103
103
|
|
|
104
104
|
if isinstance(item, dict):
|
|
@@ -279,11 +279,11 @@ class DynamoDB(DynamoDBConnection):
|
|
|
279
279
|
|
|
280
280
|
def query(
|
|
281
281
|
self,
|
|
282
|
-
key: dict | Key | ConditionBase | ComparisonCondition,
|
|
282
|
+
key: dict | Key | ConditionBase | ComparisonCondition | DynamoDBIndex,
|
|
283
|
+
table_name: str,
|
|
283
284
|
*,
|
|
284
285
|
index_name: Optional[str] = None,
|
|
285
286
|
ascending: bool = False,
|
|
286
|
-
table_name: Optional[str] = None,
|
|
287
287
|
source: Optional[str] = None,
|
|
288
288
|
strongly_consistent: bool = False,
|
|
289
289
|
projection_expression: Optional[str] = None,
|
|
@@ -305,6 +305,17 @@ class DynamoDB(DynamoDBConnection):
|
|
|
305
305
|
"""
|
|
306
306
|
|
|
307
307
|
logger.debug({"action": "query", "source": source})
|
|
308
|
+
if not key:
|
|
309
|
+
raise ValueError("Query failed: key must be provided.")
|
|
310
|
+
|
|
311
|
+
if not table_name:
|
|
312
|
+
raise ValueError("Query failed: table_name must be provided.")
|
|
313
|
+
|
|
314
|
+
if isinstance(key, DynamoDBIndex):
|
|
315
|
+
if not index_name:
|
|
316
|
+
index_name = key.name
|
|
317
|
+
# turn it into a key expected by dynamodb
|
|
318
|
+
key = key.key()
|
|
308
319
|
|
|
309
320
|
kwargs: dict = {}
|
|
310
321
|
if index_name:
|
|
@@ -330,7 +341,16 @@ class DynamoDB(DynamoDBConnection):
|
|
|
330
341
|
raise ValueError("Query failed: table_name must be provided.")
|
|
331
342
|
|
|
332
343
|
table = self.dynamodb_resource.Table(table_name)
|
|
333
|
-
response =
|
|
344
|
+
response: dict = {}
|
|
345
|
+
try:
|
|
346
|
+
response = dict(table.query(**kwargs))
|
|
347
|
+
except Exception as e: # pylint: disable=w0718
|
|
348
|
+
logger.exception(
|
|
349
|
+
{"source": f"{source}", "metric_filter": "query", "error": str(e)}
|
|
350
|
+
)
|
|
351
|
+
response = {"exception": str(e)}
|
|
352
|
+
if self.raise_on_error:
|
|
353
|
+
raise e
|
|
334
354
|
|
|
335
355
|
return response
|
|
336
356
|
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py
RENAMED
|
@@ -40,7 +40,7 @@ def exclude_indexes_from_serialization(method):
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class DynamoDBModelBase(SerializableModel):
|
|
43
|
-
"""
|
|
43
|
+
"""DynamoDb Model Base"""
|
|
44
44
|
|
|
45
45
|
T = TypeVar("T", bound="DynamoDBModelBase")
|
|
46
46
|
|
|
@@ -53,6 +53,9 @@ class DynamoDBModelBase(SerializableModel):
|
|
|
53
53
|
self.__auto_generate_projections: bool = auto_generate_projections
|
|
54
54
|
self.__actively_serializing_data__: bool = False
|
|
55
55
|
|
|
56
|
+
def serialization_in_progress(self) -> bool:
|
|
57
|
+
return self.__actively_serializing_data__
|
|
58
|
+
|
|
56
59
|
@property
|
|
57
60
|
@exclude_from_serialization
|
|
58
61
|
def indexes(self) -> DynamoDBIndexes:
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geek Cafe, LLC
|
|
3
|
+
Maintainers: Eric Wilson
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict, Optional, List, Type
|
|
9
|
+
from aws_lambda_powertools import Logger
|
|
10
|
+
from boto3_assist.dynamodb.dynamodb import DynamoDB
|
|
11
|
+
from boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
|
|
12
|
+
from boto3_assist.utilities.serialization_utility import Serialization
|
|
13
|
+
from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex
|
|
14
|
+
from boto3_assist.dynamodb.dynamodb_iservice import IDynamoDBService
|
|
15
|
+
|
|
16
|
+
logger = Logger()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DynamoDBReIndexer:
|
|
20
|
+
"""ReIndexing your database"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
table_name: str,
|
|
25
|
+
*,
|
|
26
|
+
db: Optional[DynamoDB] = None,
|
|
27
|
+
aws_profile: Optional[str] = None,
|
|
28
|
+
aws_region: Optional[str] = None,
|
|
29
|
+
aws_end_point_url: Optional[str] = None,
|
|
30
|
+
aws_access_key_id: Optional[str] = None,
|
|
31
|
+
aws_secret_access_key: Optional[str] = None,
|
|
32
|
+
):
|
|
33
|
+
self.table_name = table_name
|
|
34
|
+
self.db: DynamoDB = db or DynamoDB(
|
|
35
|
+
aws_profile=aws_profile,
|
|
36
|
+
aws_region=aws_region,
|
|
37
|
+
aws_end_point_url=aws_end_point_url,
|
|
38
|
+
aws_access_key_id=aws_access_key_id,
|
|
39
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def reindex_item(
|
|
43
|
+
self,
|
|
44
|
+
original_primary_key: dict,
|
|
45
|
+
model: DynamoDBModelBase,
|
|
46
|
+
*,
|
|
47
|
+
dry_run: bool = False,
|
|
48
|
+
inplace: bool = True,
|
|
49
|
+
leave_original_record: bool = False,
|
|
50
|
+
service_cls: Type[IDynamoDBService] | None,
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Reindex the record
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
original_primary_key (dict): The original primary key of the record to be reindexed.
|
|
57
|
+
This is either the partition_key or a composite key (partition_key, sort_key)
|
|
58
|
+
model (DynamoDBModelBase): A model instance that will be used to serialize the new keys
|
|
59
|
+
into a dictionary. It must inherit from DynamoDBModelBase
|
|
60
|
+
|
|
61
|
+
dry_run (bool, optional): Ability to log the actions without executing them. Defaults to False.
|
|
62
|
+
inplace (bool, optional): Ability to just update the indexes only.
|
|
63
|
+
No other fields will be updated, however you can't update the primary_key (partition/sort key)
|
|
64
|
+
with this action since they are immutable.
|
|
65
|
+
Defaults to True.
|
|
66
|
+
leave_original_record (bool, optional): _description_. Defaults to False.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
if inplace:
|
|
70
|
+
keys: List[DynamoDBIndex] = model.list_keys()
|
|
71
|
+
# Update the item in DynamoDB with new keys
|
|
72
|
+
self.update_item_in_dynamodb(
|
|
73
|
+
original_primary_key=original_primary_key, keys=keys, dry_run=dry_run
|
|
74
|
+
)
|
|
75
|
+
# todo: add some additional error handling here and throw a more
|
|
76
|
+
# descriptive error if they try to use a different primary
|
|
77
|
+
# pk or sk, which you can't do. If that's the case
|
|
78
|
+
else:
|
|
79
|
+
# add the new one first and optionally delete the older one
|
|
80
|
+
# once we are succesfull
|
|
81
|
+
try:
|
|
82
|
+
# save the new one first
|
|
83
|
+
service_instance: Optional[IDynamoDBService] = (
|
|
84
|
+
service_cls(db=self.db) if callable(service_cls) else None
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if service_instance:
|
|
88
|
+
service_instance.save(model=model)
|
|
89
|
+
else:
|
|
90
|
+
self.db.save(
|
|
91
|
+
item=model, table_name=self.table_name, source="reindex"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# then delete the old on
|
|
95
|
+
if not leave_original_record:
|
|
96
|
+
self.db.delete(
|
|
97
|
+
table_name=self.table_name, primary_key=original_primary_key
|
|
98
|
+
)
|
|
99
|
+
except Exception as e: # pylint: disable=broad-except
|
|
100
|
+
logger.error(str(e))
|
|
101
|
+
raise RuntimeError(str(e)) from e
|
|
102
|
+
# this gets a little more trick as we need to delete the item
|
|
103
|
+
|
|
104
|
+
def load_model(
|
|
105
|
+
self, db_item: dict, db_model: DynamoDBModelBase
|
|
106
|
+
) -> DynamoDBModelBase | None:
|
|
107
|
+
"""load the model which will serialze the dynamodb dictionary to an instance of an object"""
|
|
108
|
+
|
|
109
|
+
base_model = Serialization.map(db_item, db_model)
|
|
110
|
+
return base_model
|
|
111
|
+
|
|
112
|
+
def update_item_in_dynamodb(
|
|
113
|
+
self,
|
|
114
|
+
original_primary_key: dict,
|
|
115
|
+
keys: List[DynamoDBIndex],
|
|
116
|
+
dry_run: bool = False,
|
|
117
|
+
):
|
|
118
|
+
"""Update the dynamodb item"""
|
|
119
|
+
dictionary = self.db.helpers.keys_to_dictionary(keys=keys)
|
|
120
|
+
|
|
121
|
+
update_expression = self.build_update_expression(dictionary)
|
|
122
|
+
expression_attribute_values = self.build_expression_attribute_values(dictionary)
|
|
123
|
+
|
|
124
|
+
if not dry_run:
|
|
125
|
+
self.db.update_item(
|
|
126
|
+
table_name=self.table_name,
|
|
127
|
+
key=original_primary_key,
|
|
128
|
+
update_expression=update_expression,
|
|
129
|
+
expression_attribute_values=expression_attribute_values,
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
print("Dry run: Skipping Update item")
|
|
133
|
+
print(f"{json.dumps(original_primary_key, indent=4)}")
|
|
134
|
+
print(f"{update_expression}")
|
|
135
|
+
print(f"{json.dumps(expression_attribute_values, indent=4)}")
|
|
136
|
+
|
|
137
|
+
def build_update_expression(self, updated_keys: Dict[str, Any]) -> str:
|
|
138
|
+
"""
|
|
139
|
+
Build the expression for updating the item
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
updated_keys (Dict[str, Any]): _description_
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
str: _description_
|
|
146
|
+
"""
|
|
147
|
+
update_expression = "SET " + ", ".join(
|
|
148
|
+
f"{k} = :{k}" for k in updated_keys.keys()
|
|
149
|
+
)
|
|
150
|
+
return update_expression
|
|
151
|
+
|
|
152
|
+
def build_expression_attribute_values(
|
|
153
|
+
self, updated_keys: Dict[str, Any]
|
|
154
|
+
) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Build the expression attribute values for the update expression
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
updated_keys (Dict[str, Any]): _description_
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Dict[str, Any]: _description_
|
|
163
|
+
"""
|
|
164
|
+
expression_attribute_values = {f":{k}": v for k, v in updated_keys.items()}
|
|
165
|
+
return expression_attribute_values
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/serialization_utility.py
RENAMED
|
@@ -470,7 +470,7 @@ class Serialization:
|
|
|
470
470
|
"To work around this create a boolean (bool) property named __actively_serializing_data__. \n"
|
|
471
471
|
"e.g. self.__actively_serializing_data__: bool = False\n\n"
|
|
472
472
|
"Only issue/raise your exception if __actively_serializing_data__ is not True. \n\n"
|
|
473
|
-
"e.g. if not self.
|
|
473
|
+
"e.g. if not self.some_property and not self.__actively_serializing_data__:\n"
|
|
474
474
|
' raise ValueError("some_property must be set")\n\n'
|
|
475
475
|
"This procedure will update the property from False to True while serializing, "
|
|
476
476
|
"then back to False once serialization is complete. "
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.20.0'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from mypy_boto3_dynamodb import DynamoDBClient
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DbTestHelper:
|
|
5
|
+
|
|
6
|
+
def helper_create_mock_table(self, table_name: str, client: DynamoDBClient) -> None:
|
|
7
|
+
"""
|
|
8
|
+
Create a mock DynamoDB table.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
gs_indexes = []
|
|
12
|
+
|
|
13
|
+
for i in range(1, 11):
|
|
14
|
+
gs_indexes.append(
|
|
15
|
+
{
|
|
16
|
+
"IndexName": f"gsi{i}",
|
|
17
|
+
"KeySchema": [
|
|
18
|
+
{
|
|
19
|
+
"AttributeName": f"gsi{i}_pk",
|
|
20
|
+
"KeyType": "HASH",
|
|
21
|
+
}, # Partition key for GSI
|
|
22
|
+
{
|
|
23
|
+
"AttributeName": f"gsi{i}_sk",
|
|
24
|
+
"KeyType": "RANGE",
|
|
25
|
+
}, # Sort key for GSI
|
|
26
|
+
],
|
|
27
|
+
"Projection": {"ProjectionType": "ALL"}, # Project all attributes
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
attributes = [
|
|
32
|
+
{"AttributeName": "pk", "AttributeType": "S"},
|
|
33
|
+
{"AttributeName": "sk", "AttributeType": "S"},
|
|
34
|
+
]
|
|
35
|
+
for i in range(1, 11):
|
|
36
|
+
attributes.append({"AttributeName": f"gsi{i}_pk", "AttributeType": "S"})
|
|
37
|
+
attributes.append({"AttributeName": f"gsi{i}_sk", "AttributeType": "S"})
|
|
38
|
+
|
|
39
|
+
response = client.create_table(
|
|
40
|
+
TableName=table_name,
|
|
41
|
+
KeySchema=[
|
|
42
|
+
{"AttributeName": "pk", "KeyType": "HASH"}, # Partition key
|
|
43
|
+
{"AttributeName": "sk", "KeyType": "RANGE"}, # Sort key
|
|
44
|
+
],
|
|
45
|
+
AttributeDefinitions=attributes,
|
|
46
|
+
GlobalSecondaryIndexes=gs_indexes,
|
|
47
|
+
BillingMode="PAY_PER_REQUEST",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
print(response)
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/user_model.py
RENAMED
|
@@ -38,26 +38,26 @@ class User(DynamoDBModelBase):
|
|
|
38
38
|
primary.sort_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
|
|
39
39
|
self.indexes.add_primary(primary)
|
|
40
40
|
|
|
41
|
-
gsi0: DynamoDBIndex = DynamoDBIndex(index_name="
|
|
42
|
-
gsi0.partition_key.attribute_name = "
|
|
41
|
+
gsi0: DynamoDBIndex = DynamoDBIndex(index_name="gsi1")
|
|
42
|
+
gsi0.partition_key.attribute_name = "gsi1_pk"
|
|
43
43
|
gsi0.partition_key.value = lambda: DynamoDBKey.build_key(("users", None))
|
|
44
|
-
gsi0.sort_key.attribute_name = "
|
|
44
|
+
gsi0.sort_key.attribute_name = "gsi1_sk"
|
|
45
45
|
gsi0.sort_key.value = lambda: DynamoDBKey.build_key(("email", self.email))
|
|
46
46
|
self.indexes.add_secondary(gsi0)
|
|
47
47
|
|
|
48
|
-
gsi1: DynamoDBIndex = DynamoDBIndex(index_name="
|
|
49
|
-
gsi1.partition_key.attribute_name = "
|
|
48
|
+
gsi1: DynamoDBIndex = DynamoDBIndex(index_name="gsi2")
|
|
49
|
+
gsi1.partition_key.attribute_name = "gsi2_pk"
|
|
50
50
|
gsi1.partition_key.value = lambda: DynamoDBKey.build_key(("users", None))
|
|
51
|
-
gsi1.sort_key.attribute_name = "
|
|
51
|
+
gsi1.sort_key.attribute_name = "gsi2_sk"
|
|
52
52
|
gsi1.sort_key.value = lambda: DynamoDBKey.build_key(
|
|
53
53
|
("lastname", self.last_name), ("firstname", self.first_name)
|
|
54
54
|
)
|
|
55
55
|
self.indexes.add_secondary(gsi1)
|
|
56
56
|
|
|
57
|
-
gsi0: DynamoDBIndex = DynamoDBIndex(index_name="
|
|
58
|
-
gsi0.partition_key.attribute_name = "
|
|
57
|
+
gsi0: DynamoDBIndex = DynamoDBIndex(index_name="gsi3")
|
|
58
|
+
gsi0.partition_key.attribute_name = "gsi3_pk"
|
|
59
59
|
gsi0.partition_key.value = lambda: DynamoDBKey.build_key(("users", None))
|
|
60
|
-
gsi0.sort_key.attribute_name = "
|
|
60
|
+
gsi0.sort_key.attribute_name = "gsi3_sk"
|
|
61
61
|
gsi0.sort_key.value = lambda: DynamoDBKey.build_key(
|
|
62
62
|
("firstname", self.first_name), ("lastname", self.last_name)
|
|
63
63
|
)
|
|
@@ -32,7 +32,6 @@ class User(DynamoDBModelBase):
|
|
|
32
32
|
def __setup_indexes(self):
|
|
33
33
|
primary: DynamoDBIndex = DynamoDBIndex()
|
|
34
34
|
primary.partition_key.attribute_name = "pk"
|
|
35
|
-
# allows for a wild card search on all "sites"
|
|
36
35
|
primary.partition_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
|
|
37
36
|
primary.sort_key.attribute_name = "sk"
|
|
38
37
|
primary.sort_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py
RENAMED
|
@@ -82,43 +82,42 @@ class DynamoDBModelUnitTest(unittest.TestCase):
|
|
|
82
82
|
|
|
83
83
|
pk = user.indexes.primary.partition_key.value
|
|
84
84
|
self.assertEqual(pk, "user#123456")
|
|
85
|
-
index_name = "
|
|
85
|
+
index_name = "gsi2"
|
|
86
86
|
gsi_key = user.get_key(index_name).key()
|
|
87
87
|
|
|
88
88
|
expression = user.helpers.get_filter_expressions(gsi_key)
|
|
89
89
|
print(f"expression: {expression}")
|
|
90
90
|
keys: List[Dict] = expression.get("keys")
|
|
91
91
|
key_0: Dict = keys[0].get("key")
|
|
92
|
-
self.assertEqual(key_0.get("name"), "
|
|
92
|
+
self.assertEqual(key_0.get("name"), "gsi2_pk")
|
|
93
93
|
self.assertEqual(key_0.get("key"), "users#")
|
|
94
94
|
|
|
95
95
|
key_1: Dict = keys[1].get("key")
|
|
96
|
-
self.assertEqual(key_1.get("name"), "
|
|
96
|
+
self.assertEqual(key_1.get("name"), "gsi2_sk")
|
|
97
97
|
# we didn't populate a last name so this is correct (based on the current logic)
|
|
98
98
|
# we stop here and don't go any further
|
|
99
99
|
self.assertEqual(key_1.get("key"), "lastname#")
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
index_name = "gsi2"
|
|
101
|
+
index_name = "gsi3"
|
|
103
102
|
gsi_key = user.get_key(index_name).key()
|
|
104
103
|
# this should be mapped to gsi0
|
|
105
|
-
self.assertEqual(index_name, "
|
|
104
|
+
self.assertEqual(index_name, "gsi3")
|
|
106
105
|
|
|
107
106
|
expression = user.helpers.get_filter_expressions(gsi_key)
|
|
108
107
|
print(f"expression: {expression}")
|
|
109
108
|
keys: List[Dict] = expression.get("keys")
|
|
110
109
|
key_0: Dict = keys[0].get("key")
|
|
111
|
-
self.assertEqual(key_0.get("name"), "
|
|
110
|
+
self.assertEqual(key_0.get("name"), "gsi3_pk")
|
|
112
111
|
self.assertEqual(key_0.get("key"), "users#")
|
|
113
112
|
|
|
114
113
|
key_1: Dict = keys[1].get("key")
|
|
115
|
-
self.assertEqual(key_1.get("name"), "
|
|
114
|
+
self.assertEqual(key_1.get("name"), "gsi3_sk")
|
|
116
115
|
self.assertEqual(key_1.get("key"), "firstname#John#lastname#")
|
|
117
116
|
|
|
118
117
|
resource = user.to_resource_dictionary()
|
|
119
118
|
self.assertIsNotNone(resource)
|
|
120
119
|
|
|
121
|
-
def
|
|
120
|
+
def test_key_list(self):
|
|
122
121
|
"""Test Listing Keys"""
|
|
123
122
|
# Arrange
|
|
124
123
|
data = {
|
|
@@ -145,21 +144,21 @@ class DynamoDBModelUnitTest(unittest.TestCase):
|
|
|
145
144
|
self.assertEqual(keys[0].sort_key.attribute_name, "sk")
|
|
146
145
|
self.assertEqual(keys[0].sort_key.value, "user#123456")
|
|
147
146
|
|
|
148
|
-
self.assertEqual(keys[1].partition_key.attribute_name, "
|
|
147
|
+
self.assertEqual(keys[1].partition_key.attribute_name, "gsi1_pk")
|
|
149
148
|
self.assertEqual(keys[1].partition_key.value, "users#")
|
|
150
|
-
self.assertEqual(keys[1].sort_key.attribute_name, "
|
|
149
|
+
self.assertEqual(keys[1].sort_key.attribute_name, "gsi1_sk")
|
|
151
150
|
self.assertEqual(keys[1].sort_key.value, "email#john@example.com")
|
|
152
151
|
|
|
153
|
-
self.assertEqual(keys[2].partition_key.attribute_name, "
|
|
152
|
+
self.assertEqual(keys[2].partition_key.attribute_name, "gsi2_pk")
|
|
154
153
|
self.assertEqual(keys[2].partition_key.value, "users#")
|
|
155
|
-
self.assertEqual(keys[2].sort_key.attribute_name, "
|
|
154
|
+
self.assertEqual(keys[2].sort_key.attribute_name, "gsi2_sk")
|
|
156
155
|
self.assertEqual(keys[2].sort_key.value, "lastname#")
|
|
157
156
|
expression = user.helpers.get_filter_expressions(keys[2].key())
|
|
158
157
|
print(f"expression: {expression}")
|
|
159
158
|
|
|
160
|
-
self.assertEqual(keys[3].partition_key.attribute_name, "
|
|
159
|
+
self.assertEqual(keys[3].partition_key.attribute_name, "gsi3_pk")
|
|
161
160
|
self.assertEqual(keys[3].partition_key.value, "users#")
|
|
162
|
-
self.assertEqual(keys[3].sort_key.attribute_name, "
|
|
161
|
+
self.assertEqual(keys[3].sort_key.attribute_name, "gsi3_sk")
|
|
163
162
|
self.assertEqual(keys[3].sort_key.value, "firstname#John#lastname#")
|
|
164
163
|
|
|
165
164
|
print("stop")
|
|
@@ -186,14 +185,14 @@ class DynamoDBModelUnitTest(unittest.TestCase):
|
|
|
186
185
|
self.assertEqual(dictionary.get("pk"), "user#123456")
|
|
187
186
|
self.assertEqual(dictionary.get("sk"), "user#123456")
|
|
188
187
|
|
|
189
|
-
self.assertEqual(dictionary.get("gsi0_pk"), "users#")
|
|
190
|
-
self.assertEqual(dictionary.get("gsi0_sk"), "email#john@example.com")
|
|
191
|
-
|
|
192
188
|
self.assertEqual(dictionary.get("gsi1_pk"), "users#")
|
|
193
|
-
self.assertEqual(dictionary.get("gsi1_sk"), "
|
|
189
|
+
self.assertEqual(dictionary.get("gsi1_sk"), "email#john@example.com")
|
|
194
190
|
|
|
195
191
|
self.assertEqual(dictionary.get("gsi2_pk"), "users#")
|
|
196
|
-
self.assertEqual(dictionary.get("gsi2_sk"), "firstname#John
|
|
192
|
+
self.assertEqual(dictionary.get("gsi2_sk"), "lastname#Smith#firstname#John")
|
|
193
|
+
|
|
194
|
+
self.assertEqual(dictionary.get("gsi3_pk"), "users#")
|
|
195
|
+
self.assertEqual(dictionary.get("gsi3_sk"), "firstname#John#lastname#Smith")
|
|
197
196
|
|
|
198
197
|
print("stop")
|
|
199
198
|
|
|
@@ -218,13 +217,13 @@ class DynamoDBModelUnitTest(unittest.TestCase):
|
|
|
218
217
|
self.assertEqual(dictionary.get("pk"), "user#123456")
|
|
219
218
|
self.assertEqual(dictionary.get("sk"), "user#123456")
|
|
220
219
|
|
|
221
|
-
self.assertEqual(dictionary.get("gsi0_pk"), "users#")
|
|
222
|
-
self.assertEqual(dictionary.get("gsi0_sk"), "email#john@example.com")
|
|
223
|
-
|
|
224
220
|
self.assertEqual(dictionary.get("gsi1_pk"), "users#")
|
|
225
|
-
self.assertEqual(dictionary.get("gsi1_sk"), "
|
|
221
|
+
self.assertEqual(dictionary.get("gsi1_sk"), "email#john@example.com")
|
|
226
222
|
|
|
227
223
|
self.assertEqual(dictionary.get("gsi2_pk"), "users#")
|
|
228
|
-
self.assertEqual(dictionary.get("gsi2_sk"), "
|
|
224
|
+
self.assertEqual(dictionary.get("gsi2_sk"), "lastname#")
|
|
225
|
+
|
|
226
|
+
self.assertEqual(dictionary.get("gsi3_pk"), "users#")
|
|
227
|
+
self.assertEqual(dictionary.get("gsi3_sk"), "firstname#John#lastname#")
|
|
229
228
|
|
|
230
229
|
print("stop")
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py
RENAMED
|
@@ -31,7 +31,7 @@ class DynamoDB_SortingUnitTest(unittest.TestCase):
|
|
|
31
31
|
|
|
32
32
|
def setUp(self):
|
|
33
33
|
# load our test environment file to make sure we override any default AWS Environment Vars setup
|
|
34
|
-
# we don't want to
|
|
34
|
+
# we don't want to accidentally connect to live environments
|
|
35
35
|
# https://docs.getmoto.org/en/latest/docs/getting_started.html
|
|
36
36
|
|
|
37
37
|
self.db: DynamoDB = self.db or DynamoDB()
|
|
@@ -54,7 +54,7 @@ class DynamoDB_SortingUnitTest(unittest.TestCase):
|
|
|
54
54
|
table_name=self.__table_name, key=key, source="unittest", ascending=True
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
-
# after the insert we can sort our expected
|
|
57
|
+
# after the insert we can sort our expected values
|
|
58
58
|
slugs.sort()
|
|
59
59
|
|
|
60
60
|
self.assertIn("Items", pages)
|
|
@@ -107,7 +107,7 @@ class DynamoDB_SortingUnitTest(unittest.TestCase):
|
|
|
107
107
|
"""
|
|
108
108
|
|
|
109
109
|
return [
|
|
110
|
-
"/
|
|
110
|
+
"/zebras",
|
|
111
111
|
"/docs/docs.html",
|
|
112
112
|
"/blogs/blog.html",
|
|
113
113
|
"/blog/re-certify-your-aws-associates-and-cloud-practitioner-certifications-for-free.html",
|
|
@@ -131,7 +131,7 @@ class DynamoDB_SortingUnitTest(unittest.TestCase):
|
|
|
131
131
|
{"AttributeName": "pk", "AttributeType": "S"},
|
|
132
132
|
{"AttributeName": "sk", "AttributeType": "S"},
|
|
133
133
|
{"AttributeName": "gsi1_pk", "AttributeType": "S"},
|
|
134
|
-
{"AttributeName": "gsi1_sk", "AttributeType": "
|
|
134
|
+
{"AttributeName": "gsi1_sk", "AttributeType": "S"},
|
|
135
135
|
],
|
|
136
136
|
GlobalSecondaryIndexes=[
|
|
137
137
|
{
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geek Cafe, LLC
|
|
3
|
+
Maintainers: Eric Wilson
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import unittest
|
|
8
|
+
import moto
|
|
9
|
+
|
|
10
|
+
from tests.unit.dynamodb_tests.db_models.user_model import User
|
|
11
|
+
from boto3_assist.environment_services.environment_loader import EnvironmentLoader
|
|
12
|
+
from boto3_assist.dynamodb.dynamodb import DynamoDB
|
|
13
|
+
from tests.unit.common.db_test_helpers import DbTestHelper
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@moto.mock_aws
|
|
17
|
+
class DbQueryTest(unittest.TestCase):
|
|
18
|
+
"Serialization Tests"
|
|
19
|
+
|
|
20
|
+
def __init__(self, methodName="runTest"):
|
|
21
|
+
super().__init__(methodName)
|
|
22
|
+
|
|
23
|
+
ev: EnvironmentLoader = EnvironmentLoader()
|
|
24
|
+
# NOTE: you need to make sure the the env file below exists or you will get an error
|
|
25
|
+
ev.load_environment_file(file_name=".env.unittest")
|
|
26
|
+
self.__table_name = "mock_test_table"
|
|
27
|
+
|
|
28
|
+
self.db: DynamoDB = DynamoDB()
|
|
29
|
+
|
|
30
|
+
def setUp(self):
|
|
31
|
+
# load our test environment file to make sure we override any default AWS Environment Vars setup
|
|
32
|
+
# we don't want to accidentally connect to live environments
|
|
33
|
+
# https://docs.getmoto.org/en/latest/docs/getting_started.html
|
|
34
|
+
|
|
35
|
+
self.db: DynamoDB = self.db or DynamoDB()
|
|
36
|
+
DbTestHelper().helper_create_mock_table(self.__table_name, self.db.client)
|
|
37
|
+
print("Setup Complete")
|
|
38
|
+
|
|
39
|
+
def test_query_user(self):
|
|
40
|
+
|
|
41
|
+
# create a few users
|
|
42
|
+
for i in range(1, 11):
|
|
43
|
+
user = User(i)
|
|
44
|
+
|
|
45
|
+
user.first_name = f"first_{i}"
|
|
46
|
+
user.last_name = f"last_{i}"
|
|
47
|
+
user.email = f"email_{i}@example.com"
|
|
48
|
+
response = self.db.save(table_name=self.__table_name, item=user)
|
|
49
|
+
|
|
50
|
+
self.assertEqual(response["ResponseMetadata"]["HTTPStatusCode"], 200)
|
|
51
|
+
|
|
52
|
+
# query for all users
|
|
53
|
+
user = User()
|
|
54
|
+
key = user.get_key("gsi1")
|
|
55
|
+
response = self.db.query(table_name=self.__table_name, key=key)
|
|
56
|
+
items = response["Items"]
|
|
57
|
+
self.assertEqual(len(items), 10)
|
|
58
|
+
|
|
59
|
+
# query for one user
|
|
60
|
+
user = User()
|
|
61
|
+
user.email = "email_1@example.com"
|
|
62
|
+
# use the same index but this time we have an email address to add to the filter
|
|
63
|
+
response = self.db.query(table_name=self.__table_name, key=user.get_key("gsi1"))
|
|
64
|
+
|
|
65
|
+
items = response["Items"]
|
|
66
|
+
self.assertEqual(len(items), 1)
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py
RENAMED
|
@@ -8,7 +8,7 @@ import unittest
|
|
|
8
8
|
from typing import Optional, List
|
|
9
9
|
|
|
10
10
|
from src.boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
|
|
11
|
-
from
|
|
11
|
+
from boto3_assist.dynamodb.dynamodb_re_indexer import DynamoDBReIndexer
|
|
12
12
|
from src.boto3_assist.dynamodb.dynamodb_key import DynamoDBKey
|
|
13
13
|
from src.boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex
|
|
14
14
|
|
|
@@ -120,12 +120,12 @@ class ReindexTest(unittest.TestCase):
|
|
|
120
120
|
user: User = User().map(data)
|
|
121
121
|
keys: List[DynamoDBKey] = user.list_keys()
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
re_indexer: DynamoDBReIndexer = DynamoDBReIndexer("dummy_table")
|
|
124
124
|
|
|
125
125
|
dictionary = user.helpers.keys_to_dictionary(keys=keys)
|
|
126
126
|
|
|
127
|
-
update_expression =
|
|
128
|
-
expression_attribute_values =
|
|
127
|
+
update_expression = re_indexer.build_update_expression(dictionary)
|
|
128
|
+
expression_attribute_values = re_indexer.build_expression_attribute_values(
|
|
129
129
|
dictionary
|
|
130
130
|
)
|
|
131
131
|
|
|
@@ -71,7 +71,7 @@ class TestLambdaEventInfo(unittest.TestCase):
|
|
|
71
71
|
self.assertEqual(result, "access")
|
|
72
72
|
|
|
73
73
|
@patch("boto3_assist.cognito.cognito_authorizer.CognitoCustomAuthorizer.parse_jwt")
|
|
74
|
-
def
|
|
74
|
+
def test_get_value_from_token(self, mock_parse_jwt):
|
|
75
75
|
"""
|
|
76
76
|
Test that values are correctly extracted from the JWT token.
|
|
77
77
|
"""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.18.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/examples/dynamodb/services/order_item_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py
RENAMED
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/environment_services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/securityhub/securityhub_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py
RENAMED
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/src/boto3_assist/utilities/dictionary_utility.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/integration/cross_account_connection_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/content_block.py
RENAMED
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/cms/template.py
RENAMED
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/dynamodb_tests/db_models/simple_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/serializable_model_user_test.py
RENAMED
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/models_tests/serializable_model_wide_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/parameter_store/parameter_store_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/session_tests/test_boto3_session_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{boto3_assist-0.18.0 → boto3_assist-0.20.0}/tests/unit/utilities/serialization_utility_test.py
RENAMED
|
File without changes
|
|
File without changes
|