boto3-assist 0.18.0__tar.gz → 0.19.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.
Files changed (142) hide show
  1. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.vscode/settings.json +2 -1
  2. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/PKG-INFO +1 -1
  3. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/pyproject.toml +1 -1
  4. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb.py +31 -11
  5. boto3_assist-0.18.0/src/boto3_assist/dynamodb/dynamodb_reindexer.py → boto3_assist-0.19.0/src/boto3_assist/dynamodb/dynamodb_re_indexer.py +2 -2
  6. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/serialization_utility.py +1 -1
  7. boto3_assist-0.19.0/src/boto3_assist/version.py +1 -0
  8. boto3_assist-0.19.0/tests/unit/common/db_test_helpers.py +50 -0
  9. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/user_model.py +9 -9
  10. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/user_required_fields_model.py +0 -1
  11. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py +24 -25
  12. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py +4 -4
  13. boto3_assist-0.19.0/tests/unit/dynamodb_tests/dynamodb_query_test.py +66 -0
  14. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py +4 -4
  15. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/lambda_tests/event_info_test.py +1 -1
  16. boto3_assist-0.18.0/src/boto3_assist/version.py +0 -1
  17. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.env.docker +0 -0
  18. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.env.docker.001 +0 -0
  19. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.env.docker.nosql.workbench +0 -0
  20. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.env.unittest +0 -0
  21. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.gitignore +0 -0
  22. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.vscode/launch.json +0 -0
  23. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/.vscode/tasks.json +0 -0
  24. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/LICENSE-EXPLAINED.txt +0 -0
  25. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/LICENSE.txt +0 -0
  26. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/README.md +0 -0
  27. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/aws_regions_with_status.csv +0 -0
  28. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/aws_regions_with_status.json +0 -0
  29. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/devops/build.py +0 -0
  30. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/devops/readme.md +0 -0
  31. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/__init__.py +0 -0
  32. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/cloudwatch/log_report.py +0 -0
  33. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/models/order_item_model.py +0 -0
  34. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/models/order_model.py +0 -0
  35. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/models/product_model.py +0 -0
  36. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/models/user_model.py +0 -0
  37. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/models/user_post_model.py +0 -0
  38. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/order_example/main.py +0 -0
  39. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/order_example/products.json +0 -0
  40. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/order_item_service.py +0 -0
  41. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/order_service.py +0 -0
  42. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/product_service.py +0 -0
  43. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/table_service.py +0 -0
  44. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/user_post_service.py +0 -0
  45. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/user_service.py +0 -0
  46. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
  47. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
  48. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/dynamodb/user_post_example/main.py +0 -0
  49. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/examples/ec2/regions_report.py +0 -0
  50. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/module-headers.txt +0 -0
  51. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/mypy.ini +0 -0
  52. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/requirements-dev.txt +0 -0
  53. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/requirements.txt +0 -0
  54. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/run-checks.sh +0 -0
  55. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/run_unit_tests.sh +0 -0
  56. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/__init__.py +0 -0
  57. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/aws_config.py +0 -0
  58. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
  59. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
  60. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/boto3session.py +0 -0
  61. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
  62. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
  63. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
  64. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
  65. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
  66. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
  67. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
  68. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
  69. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
  70. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/cognito/user.py +0 -0
  71. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/connection.py +0 -0
  72. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/connection_tracker.py +0 -0
  73. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
  74. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
  75. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
  76. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
  77. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
  78. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
  79. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +0 -0
  80. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
  81. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
  82. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
  83. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/readme.md +0 -0
  84. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
  85. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
  86. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/environment_services/__init__.py +0 -0
  87. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
  88. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
  89. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
  90. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/http_status_codes.py +0 -0
  91. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/models/serializable_model.py +0 -0
  92. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/role_assumption_mixin.py +0 -0
  93. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/s3/s3.py +0 -0
  94. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
  95. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/s3/s3_connection.py +0 -0
  96. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
  97. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/s3/s3_object.py +0 -0
  98. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
  99. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
  100. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/session_setup_mixin.py +0 -0
  101. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/ssm/connection.py +0 -0
  102. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
  103. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
  104. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
  105. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/file_operations.py +0 -0
  106. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/http_utility.py +0 -0
  107. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
  108. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
  109. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/src/boto3_assist/utilities/string_utility.py +0 -0
  110. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/__init__.py +0 -0
  111. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/integration/cross_account_connection_test.py +0 -0
  112. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/integration/tenant.py +0 -0
  113. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/integration/tenant_services.py +0 -0
  114. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/aws_config_test.py +0 -0
  115. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/__init__.py +0 -0
  116. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/cms/base.py +0 -0
  117. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/cms/content_block.py +0 -0
  118. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/cms/page.py +0 -0
  119. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/cms/template.py +0 -0
  120. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/db_models/simple_model.py +0 -0
  121. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/dynamodb_model_projections_test.py +0 -0
  122. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/dynamodb_tests/dynamodb_model_serializtion_test.py +0 -0
  123. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/examples_test/__init__.py +0 -0
  124. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/examples_test/user_service_test.py +0 -0
  125. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/lambda_tests/__init__.py +0 -0
  126. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/__init__.py +0 -0
  127. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/models/person.py +0 -0
  128. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/models/user.py +0 -0
  129. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/serializable_model_person_test.py +0 -0
  130. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/serializable_model_user_test.py +0 -0
  131. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/models_tests/serializable_model_wide_test.py +0 -0
  132. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/parameter_store/__init__.py +0 -0
  133. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/parameter_store/parameter_store_test.py +0 -0
  134. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/s3/__init__.py +0 -0
  135. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/s3/files/test.txt +0 -0
  136. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/s3/s3_event_data_test.py +0 -0
  137. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/s3/s3_file_delete_test.py +0 -0
  138. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/s3/s3_file_upload_test.py +0 -0
  139. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/session_tests/test_boto3_session_manager.py +0 -0
  140. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/utilities/__init__.py +0 -0
  141. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/utilities/serialization_utility_test.py +0 -0
  142. {boto3_assist-0.18.0 → boto3_assist-0.19.0}/tests/unit/utilities/string_utility_test.py +0 -0
@@ -27,7 +27,8 @@
27
27
  "editor.defaultFormatter": "ms-python.black-formatter",
28
28
  },
29
29
  "cSpell.words": [
30
- "addopts"
30
+ "addopts",
31
+ "geekcafe"
31
32
  ],
32
33
 
33
34
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.18.0
3
+ Version: 0.19.0
4
4
  Summary: Additional boto3 wrappers to make your life a little easier
5
5
  Author-email: Eric Wilson <boto3-assist@geekcafe.com>
6
6
  License-File: LICENSE-EXPLAINED.txt
@@ -15,7 +15,7 @@ addopts = "-m 'not integration'"
15
15
 
16
16
  [project]
17
17
  name = "boto3_assist"
18
- version = "0.18.0"
18
+ version = "0.19.0"
19
19
 
20
20
  authors = [
21
21
  { name="Eric Wilson", email="boto3-assist@geekcafe.com" }
@@ -15,11 +15,11 @@ from boto3.dynamodb.conditions import (
15
15
  ComparisonCondition,
16
16
  ConditionBase,
17
17
  )
18
- from boto3_assist.dynamodb.dynamodb_connection import DynamoDBConnection
19
- from boto3_assist.dynamodb.dynamodb_helpers import DynamoDBHelpers
20
- from boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
21
- from boto3_assist.utilities.string_utility import StringUtility
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 Dictionay Object or DynamoDBModelBase. Supports the "client" or
72
+ item (dict): DynamoDB Dictionary Object or DynamoDBModelBase. Supports the "client" or
73
73
  "resource" syntax
74
- table_name (str): The DyamoDb Table Name
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 occured during model converation. The entry was not saved. "
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 = dict(table.query(**kwargs))
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
 
@@ -16,8 +16,8 @@ from boto3_assist.dynamodb.dynamodb_iservice import IDynamoDBService
16
16
  logger = Logger()
17
17
 
18
18
 
19
- class DynamoDBReindexer:
20
- """Reindexing your database"""
19
+ class DynamoDBReIndexer:
20
+ """ReIndexing your database"""
21
21
 
22
22
  def __init__(
23
23
  self,
@@ -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.some_propert and not self.__actively_serializing_data__:\n"
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.19.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)
@@ -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="gsi0")
42
- gsi0.partition_key.attribute_name = "gsi0_pk"
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 = "gsi0_sk"
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="gsi1")
49
- gsi1.partition_key.attribute_name = "gsi1_pk"
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 = "gsi1_sk"
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="gsi2")
58
- gsi0.partition_key.attribute_name = "gsi2_pk"
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 = "gsi2_sk"
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))
@@ -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 = "gsi1"
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"), "gsi1_pk")
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"), "gsi1_sk")
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
- ### gsi3 mapped to a name of gsi2
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, "gsi2")
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"), "gsi2_pk")
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"), "gsi2_sk")
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 test_keylist(self):
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, "gsi0_pk")
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, "gsi0_sk")
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, "gsi1_pk")
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, "gsi1_sk")
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, "gsi2_pk")
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, "gsi2_sk")
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"), "lastname#Smith#firstname#John")
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#lastname#Smith")
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"), "lastname#")
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"), "firstname#John#lastname#")
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")
@@ -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 accidently connec to live environments
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 vaues
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
- "/zerbras",
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": "N"},
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)
@@ -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 src.boto3_assist.dynamodb.dynamodb_reindexer import DynamoDBReindexer
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
- reindexer: DynamoDBReindexer = DynamoDBReindexer("dummy_table")
123
+ re_indexer: DynamoDBReIndexer = DynamoDBReIndexer("dummy_table")
124
124
 
125
125
  dictionary = user.helpers.keys_to_dictionary(keys=keys)
126
126
 
127
- update_expression = reindexer.build_update_expression(dictionary)
128
- expression_attribute_values = reindexer.build_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 testget_value_from_token(self, mock_parse_jwt):
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