boto3-assist 0.15.0__tar.gz → 0.17.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.15.0 → boto3_assist-0.17.0}/.gitignore +2 -1
  2. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/PKG-INFO +1 -1
  3. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/models/order_item_model.py +7 -7
  4. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/models/order_model.py +7 -7
  5. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/models/product_model.py +9 -7
  6. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/models/user_model.py +7 -7
  7. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/models/user_post_model.py +2 -2
  8. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/pyproject.toml +1 -1
  9. boto3_assist-0.17.0/src/boto3_assist/aws_config.py +199 -0
  10. boto3_assist-0.17.0/src/boto3_assist/dynamodb/troubleshooting.md +7 -0
  11. boto3_assist-0.17.0/src/boto3_assist/session_setup_mixin.py +70 -0
  12. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/serialization_utility.py +9 -2
  13. boto3_assist-0.17.0/src/boto3_assist/version.py +1 -0
  14. boto3_assist-0.17.0/tests/unit/aws_config_test.py +81 -0
  15. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/cms/base.py +1 -1
  16. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/cms/content_block.py +8 -8
  17. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/cms/page.py +9 -9
  18. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/cms/template.py +11 -9
  19. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/user_model.py +6 -6
  20. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/user_required_fields_model.py +6 -6
  21. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py +4 -4
  22. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/dynamodb_model_projections_test.py +5 -5
  23. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/dynamodb_model_serializtion_test.py +4 -4
  24. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py +2 -2
  25. boto3_assist-0.17.0/tests/unit/session_tests/test_boto3_session_manager.py +177 -0
  26. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/utilities/serialization_utility_test.py +3 -3
  27. boto3_assist-0.15.0/src/boto3_assist/dynamodb/troubleshooting.md +0 -5
  28. boto3_assist-0.15.0/src/boto3_assist/session_setup_mixin.py +0 -29
  29. boto3_assist-0.15.0/src/boto3_assist/version.py +0 -1
  30. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.env.docker +0 -0
  31. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.env.docker.001 +0 -0
  32. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.env.docker.nosql.workbench +0 -0
  33. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.env.unittest +0 -0
  34. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.vscode/launch.json +0 -0
  35. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.vscode/settings.json +0 -0
  36. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/.vscode/tasks.json +0 -0
  37. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/LICENSE-EXPLAINED.txt +0 -0
  38. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/LICENSE.txt +0 -0
  39. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/README.md +0 -0
  40. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/aws_regions_with_status.csv +0 -0
  41. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/aws_regions_with_status.json +0 -0
  42. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/devops/build.py +0 -0
  43. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/devops/readme.md +0 -0
  44. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/__init__.py +0 -0
  45. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/cloudwatch/log_report.py +0 -0
  46. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/order_example/main.py +0 -0
  47. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/order_example/products.json +0 -0
  48. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/order_item_service.py +0 -0
  49. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/order_service.py +0 -0
  50. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/product_service.py +0 -0
  51. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/table_service.py +0 -0
  52. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/user_post_service.py +0 -0
  53. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/user_service.py +0 -0
  54. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
  55. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
  56. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/dynamodb/user_post_example/main.py +0 -0
  57. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/examples/ec2/regions_report.py +0 -0
  58. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/module-headers.txt +0 -0
  59. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/mypy.ini +0 -0
  60. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/requirements-dev.txt +0 -0
  61. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/requirements.txt +0 -0
  62. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/run-checks.sh +0 -0
  63. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/run_unit_tests.sh +0 -0
  64. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/__init__.py +0 -0
  65. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
  66. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
  67. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/boto3session.py +0 -0
  68. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
  69. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
  70. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
  71. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
  72. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
  73. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
  74. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
  75. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
  76. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
  77. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/cognito/user.py +0 -0
  78. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/connection.py +0 -0
  79. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/connection_tracker.py +0 -0
  80. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb.py +0 -0
  81. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
  82. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
  83. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
  84. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
  85. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
  86. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
  87. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +0 -0
  88. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
  89. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
  90. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
  91. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
  92. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/dynamodb/readme.md +0 -0
  93. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
  94. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/environment_services/__init__.py +0 -0
  95. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
  96. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
  97. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
  98. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/http_status_codes.py +0 -0
  99. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/models/serializable_model.py +0 -0
  100. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/role_assumption_mixin.py +0 -0
  101. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/s3/s3.py +0 -0
  102. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
  103. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/s3/s3_connection.py +0 -0
  104. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
  105. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/s3/s3_object.py +0 -0
  106. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
  107. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
  108. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/ssm/connection.py +0 -0
  109. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
  110. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
  111. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
  112. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/file_operations.py +0 -0
  113. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/http_utility.py +0 -0
  114. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
  115. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
  116. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/src/boto3_assist/utilities/string_utility.py +0 -0
  117. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/__init__.py +0 -0
  118. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/integration/cross_account_connection_test.py +0 -0
  119. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/integration/tenant.py +0 -0
  120. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/integration/tenant_services.py +0 -0
  121. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/__init__.py +0 -0
  122. {boto3_assist-0.15.0/tests/unit/dynamodb_tests/dbmodels → boto3_assist-0.17.0/tests/unit/dynamodb_tests/db_models}/simple_model.py +0 -0
  123. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py +0 -0
  124. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/examples_test/__init__.py +0 -0
  125. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/examples_test/user_service_test.py +0 -0
  126. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/lambda_tests/__init__.py +0 -0
  127. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/lambda_tests/event_info_test.py +0 -0
  128. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/__init__.py +0 -0
  129. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/models/person.py +0 -0
  130. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/models/user.py +0 -0
  131. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/serializable_model_person_test.py +0 -0
  132. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/serializable_model_user_test.py +0 -0
  133. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/models_tests/serializable_model_wide_test.py +0 -0
  134. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/parameter_store/__init__.py +0 -0
  135. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/parameter_store/parameter_store_test.py +0 -0
  136. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/s3/__init__.py +0 -0
  137. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/s3/files/test.txt +0 -0
  138. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/s3/s3_event_data_test.py +0 -0
  139. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/s3/s3_file_delete_test.py +0 -0
  140. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/s3/s3_file_upload_test.py +0 -0
  141. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/utilities/__init__.py +0 -0
  142. {boto3_assist-0.15.0 → boto3_assist-0.17.0}/tests/unit/utilities/string_utility_test.py +0 -0
@@ -164,4 +164,5 @@ cython_debug/
164
164
  .imports
165
165
  .env.development
166
166
  .unittest
167
- .unittests
167
+ .unittests
168
+ .outputs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.15.0
3
+ Version: 0.17.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
@@ -36,15 +36,15 @@ class OrderItem(DynamoDBModelBase):
36
36
  # the primary key will be made off of the
37
37
  # order.id and this item.id
38
38
  # this will allow for a 1 to many search on the items related to an order
39
- primay: DynamoDBIndex = DynamoDBIndex()
40
- primay.name = "primary"
39
+ primary: DynamoDBIndex = DynamoDBIndex()
40
+ primary.name = "primary"
41
41
  # create a partition key off of the order key
42
- primay.partition_key.attribute_name = "pk"
43
- primay.partition_key.value = lambda: DynamoDBKey.build_key(
42
+ primary.partition_key.attribute_name = "pk"
43
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(
44
44
  ("order", self.order_id)
45
45
  )
46
46
 
47
47
  # create the sort key off of this items id
48
- primay.sort_key.attribute_name = "sk"
49
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("item", self.id))
50
- self.indexes.add_primary(primay)
48
+ primary.sort_key.attribute_name = "sk"
49
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("item", self.id))
50
+ self.indexes.add_primary(primary)
@@ -43,13 +43,13 @@ class Order(DynamoDBModelBase):
43
43
 
44
44
  def __setup_indexes(self):
45
45
  # user id
46
- primay: DynamoDBIndex = DynamoDBIndex()
47
- primay.name = "primary"
48
- primay.partition_key.attribute_name = "pk"
49
- primay.partition_key.value = lambda: DynamoDBKey.build_key(("order", self.id))
50
- primay.sort_key.attribute_name = "sk"
51
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("order", self.id))
52
- self.indexes.add_primary(primay)
46
+ primary: DynamoDBIndex = DynamoDBIndex()
47
+ primary.name = "primary"
48
+ primary.partition_key.attribute_name = "pk"
49
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(("order", self.id))
50
+ primary.sort_key.attribute_name = "sk"
51
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("order", self.id))
52
+ self.indexes.add_primary(primary)
53
53
 
54
54
  # all orders on a given day, sort by created date
55
55
  self.indexes.add_secondary(
@@ -33,13 +33,15 @@ class Product(DynamoDBModelBase):
33
33
  return f"{self.name} - ${self.price}"
34
34
 
35
35
  def __setup_indexes(self):
36
- primay: DynamoDBIndex = DynamoDBIndex()
37
- primay.name = "primary"
38
- primay.partition_key.attribute_name = "pk"
39
- primay.partition_key.value = lambda: DynamoDBKey.build_key(("product", self.id))
40
- primay.sort_key.attribute_name = "sk"
41
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("product", self.id))
42
- self.indexes.add_primary(primay)
36
+ primary: DynamoDBIndex = DynamoDBIndex()
37
+ primary.name = "primary"
38
+ primary.partition_key.attribute_name = "pk"
39
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(
40
+ ("product", self.id)
41
+ )
42
+ primary.sort_key.attribute_name = "sk"
43
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("product", self.id))
44
+ self.indexes.add_primary(primary)
43
45
 
44
46
  self.indexes.add_secondary(
45
47
  DynamoDBIndex(
@@ -31,13 +31,13 @@ class User(DynamoDBModelBase):
31
31
 
32
32
  def __setup_indexes(self):
33
33
  # user id
34
- primay: DynamoDBIndex = DynamoDBIndex()
35
- primay.name = "primary"
36
- primay.partition_key.attribute_name = "pk"
37
- primay.partition_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
38
- primay.sort_key.attribute_name = "sk"
39
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
40
- self.indexes.add_primary(primay)
34
+ primary: DynamoDBIndex = DynamoDBIndex()
35
+ primary.name = "primary"
36
+ primary.partition_key.attribute_name = "pk"
37
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
38
+ primary.sort_key.attribute_name = "sk"
39
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("user", self.id))
40
+ self.indexes.add_primary(primary)
41
41
 
42
42
  # find all users by email address
43
43
  gsi0: DynamoDBIndex = DynamoDBIndex(
@@ -33,7 +33,7 @@ class UserPost(DynamoDBModelBase):
33
33
  self.__setup_indexes()
34
34
 
35
35
  def __setup_indexes(self):
36
- primay: DynamoDBIndex = DynamoDBIndex(
36
+ primary: DynamoDBIndex = DynamoDBIndex(
37
37
  index_name="primary",
38
38
  partition_key=DynamoDBKey(
39
39
  attribute_name="pk",
@@ -44,7 +44,7 @@ class UserPost(DynamoDBModelBase):
44
44
  value=lambda: f"post#{self.slug if self.slug else ''}",
45
45
  ),
46
46
  )
47
- self.indexes.add_primary(primay)
47
+ self.indexes.add_primary(primary)
48
48
 
49
49
  gsi0: DynamoDBIndex = DynamoDBIndex(
50
50
  index_name="gsi0",
@@ -15,7 +15,7 @@ addopts = "-m 'not integration'"
15
15
 
16
16
  [project]
17
17
  name = "boto3_assist"
18
- version = "0.15.0"
18
+ version = "0.17.0"
19
19
 
20
20
  authors = [
21
21
  { name="Eric Wilson", email="boto3-assist@geekcafe.com" }
@@ -0,0 +1,199 @@
1
+ import os
2
+ from pathlib import Path
3
+ import configparser
4
+ from typing import Literal, Optional
5
+ from boto3_assist.utilities.serialization_utility import SerializableModel
6
+
7
+
8
+ class AWSConfigProfile(SerializableModel):
9
+ def __init__(
10
+ self, region: Optional[str] = "us-east-1", output: Optional[str] = "json"
11
+ ):
12
+
13
+ self.region: Optional[str] = region
14
+ self.output: Optional[str] = output
15
+
16
+ self.aws_access_key_id: Optional[str] = None
17
+ self.aws_secret_access_key: Optional[str] = None
18
+ self.aws_session_token: Optional[str] = None
19
+
20
+ self.sso_session: Optional[str] = None
21
+ self.sso_account_id: Optional[str] = None
22
+ self.sso_role_name: Optional[str] = None
23
+
24
+ self.credential_process: Optional[str] = None
25
+ self.credential_source: Optional[str] = None
26
+ self.role_arn: Optional[str] = None
27
+ self.source_profile: Optional[str] = None
28
+ self.external_id: Optional[str] = None
29
+ self.role_session_name: Optional[str] = None
30
+ self.duration_seconds: Optional[str] = None
31
+
32
+
33
+ class AWSConfigSSOSession(SerializableModel):
34
+ def __init__(
35
+ self,
36
+ sso_start_url: Optional[str] = None,
37
+ sso_region: Optional[str] = None,
38
+ sso_registration_scopes: Optional[str] = None,
39
+ ):
40
+ self.sso_start_url: Optional[str] = sso_start_url
41
+ self.sso_region: Optional[str] = sso_region
42
+ self.sso_registration_scopes: Optional[str] = sso_registration_scopes
43
+
44
+
45
+ class AWSConfig:
46
+ """
47
+ Performs Operations on an AWS Config
48
+ """
49
+
50
+ def __init__(self):
51
+ pass
52
+
53
+ def get_path(self) -> Path:
54
+ """
55
+ Returns the path to the AWS config file, honoring AWS_CONFIG_FILE
56
+ and falling back to ~/.aws/config (or %USERPROFILE%\.aws\config on Windows).
57
+ """
58
+ # 1) Check for explicit override
59
+ env_path = os.environ.get("AWS_CONFIG_FILE")
60
+ if env_path:
61
+ return Path(env_path).expanduser()
62
+
63
+ # 2) Default location
64
+ return os.path.join(Path.home(), ".aws", "config")
65
+
66
+ def path_exists(self) -> bool:
67
+ path = self.get_path()
68
+
69
+ return os.path.isfile(path)
70
+
71
+ def has_profile(self, profile_name: str) -> bool:
72
+ config = configparser.ConfigParser()
73
+ self.read_section(profile_name, config)
74
+ return profile_name in config.sections()
75
+
76
+ def upsert_profile(
77
+ self,
78
+ name: str,
79
+ profile: AWSConfigProfile,
80
+ config_path: Optional[str] = None,
81
+ ):
82
+ self.write_section(name, profile, config_path)
83
+
84
+ def upsert_sso_session(
85
+ self,
86
+ profile_name: str,
87
+ sso_session: AWSConfigSSOSession,
88
+ profile: AWSConfigProfile | None = None,
89
+ config_path: Optional[str] = None,
90
+ ):
91
+ """
92
+ Insert / Update the SSO Session block in the aws config
93
+ Args:
94
+ Name (str): Required. Specifies the profile name. This is the init key
95
+ which is added to the section for both sso-session and profile
96
+ e.g. [profile {profile_name}] or [sso-session {profile_name}]
97
+ sso_session (AWSConfigSSOSession): Defines the values written to this session block
98
+ profile (AWSConfigProfile): Defines the values written to the profile block. Typically
99
+ you will need a profile block along with a session block when using sso-session
100
+
101
+
102
+ As a general rule you will typically need to build the following:
103
+ [profile {profile-name}]
104
+ sso_session = {optionally-use-profile-name}
105
+ sso_account_id = {aws-acount-id}
106
+ sso_role_name = {sso-role}
107
+ region = {region}
108
+ output = {output}
109
+
110
+ [sso-session {profile-name}]
111
+ sso_start_url = {sso_start_url} e.g. https://account-alias.awsapps.com/start
112
+ sso_region = {region}
113
+ sso_registration_scopes = {scopes}
114
+
115
+ """
116
+
117
+ if not profile_name:
118
+ raise ValueError("Name is required")
119
+
120
+ self.write_section(profile_name, sso_session, config_path)
121
+
122
+ if profile:
123
+ self.write_section(profile_name, profile, config_path)
124
+
125
+ def write_section(
126
+ self,
127
+ profile_name: str | None,
128
+ section: AWSConfigProfile | AWSConfigSSOSession,
129
+ config_path: Optional[str] = None,
130
+ ):
131
+ config = configparser.ConfigParser()
132
+ path = config_path or self.get_path()
133
+
134
+ if self.path_exists():
135
+ config.read(path) # or any INI file path
136
+
137
+ section_key = ""
138
+
139
+ if profile_name.startswith("sso-session "):
140
+ profile_name = profile_name.replace("sso-session ", "")
141
+ if profile_name.startswith("profile "):
142
+ profile_name = profile_name.replace("profile ", "")
143
+
144
+ if isinstance(section, AWSConfigProfile):
145
+ if profile_name:
146
+ section_key = f"profile {profile_name}"
147
+ else:
148
+ section_key = "default"
149
+ elif isinstance(section, AWSConfigSSOSession):
150
+ section_key = f"sso-session {profile_name}"
151
+ else:
152
+ raise ValueError("Invalid section type")
153
+
154
+ config = self._write_section(section_key, section, config)
155
+
156
+ with open(path, "w", encoding="utf-8") as cfg_file:
157
+ config.write(cfg_file)
158
+
159
+ def _write_section(
160
+ self,
161
+ section_key: str,
162
+ section: AWSConfigProfile | AWSConfigSSOSession,
163
+ config: configparser.ConfigParser,
164
+ ) -> configparser.ConfigParser:
165
+
166
+ # always start with a "fresh" section
167
+ config[section_key] = {}
168
+
169
+ section_dictionary = section.to_dictionary()
170
+ for key, value in section_dictionary.items():
171
+ if value is not None:
172
+ config[section_key][key] = value
173
+
174
+ return config
175
+
176
+ def read_section(
177
+ self,
178
+ profile_name: Optional[str] = None,
179
+ config_path: Optional[str] = None,
180
+ section_type: Literal["profile", "sso-session"] = "profile",
181
+ ) -> configparser.SectionProxy:
182
+ config = configparser.ConfigParser()
183
+ if not config_path:
184
+ config_path = self.get_path()
185
+
186
+ if not os.path.isfile(config_path):
187
+ return config
188
+
189
+ config.read(config_path)
190
+ profile_ini = f"{section_type} {profile_name}"
191
+ if profile_ini in config:
192
+ profile = config[profile_ini]
193
+ return profile
194
+
195
+ if profile_name in config:
196
+ profile = config[profile_name]
197
+ return profile
198
+
199
+ return {}
@@ -0,0 +1,7 @@
1
+
2
+
3
+ 1. Unable to locate credentials. You can configure credentials by running "aws configure".
4
+
5
+ - out of the blue, this started happening
6
+ - depending on how you are connecting boto3 may require a credentials file
7
+ - running "aws configure" and simply puttting in some garbage may fix the issue.
@@ -0,0 +1,70 @@
1
+ """
2
+ Geek Cafe, LLC
3
+ Maintainers: Eric Wilson
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+
7
+ import os
8
+ import boto3
9
+ from typing import Optional
10
+ from boto3_assist.aws_config import AWSConfig
11
+
12
+
13
+ class SessionSetupMixin:
14
+ def _create_base_session(
15
+ self,
16
+ aws_profile: Optional[str],
17
+ aws_region: Optional[str],
18
+ aws_access_key_id: Optional[str],
19
+ aws_secret_access_key: Optional[str],
20
+ aws_session_token: Optional[str],
21
+ ) -> boto3.Session:
22
+ try:
23
+ return boto3.Session(
24
+ profile_name=aws_profile,
25
+ region_name=aws_region,
26
+ aws_access_key_id=aws_access_key_id,
27
+ aws_secret_access_key=aws_secret_access_key,
28
+ aws_session_token=aws_session_token,
29
+ )
30
+ except Exception as e:
31
+
32
+ error_message = f"Failed to create boto3 session "
33
+ if "profile" in str(e).lower():
34
+ error_message += " due to a profile error "
35
+
36
+ error_message += f" with profile '{aws_profile}'."
37
+ config: AWSConfig = AWSConfig()
38
+ if not config.path_exists():
39
+ error_message += (
40
+ f" The AWS config file '{config.get_path()}' was not found. "
41
+ "Please ensure that the AWS CLI is installed and configured correctly. "
42
+ "You can install the AWS CLI by running 'pip install awscli' or 'pip install awscli --upgrade'. "
43
+ )
44
+
45
+ if (
46
+ os.getenv("HOME") == "/tmp"
47
+ or os.getenv("AWS_CONFIG_FILE") == "/tmp"
48
+ ):
49
+ error_message += (
50
+ f'The environment HOME path is set to {os.getenv("HOME")}. '
51
+ )
52
+ if os.getenv("AWS_CONFIG_FILE"):
53
+ error_message += (
54
+ f"The environment AWS_CONFIG_FILE is set to {os.getenv('AWS_CONFIG_FILE')}. "
55
+ "The AWS_CONFIG_FILE overrides the HOME directory and path. "
56
+ )
57
+
58
+ error_message += (
59
+ "If you are running this locally and expecting it to be in your users home "
60
+ "directory, you may need to set the HOME or AWS_CONFIG_FILE environment variable manually. "
61
+ "There could be other actions such as a Lambda environment resetting the path to /tmp. "
62
+ "If you are running in a GitHub Actions environment, "
63
+ "you may need to set the HOME or AWS_CONFIG_FILE environment variable to '/home/runner'. "
64
+ )
65
+ elif not config.has_profile(aws_profile):
66
+ error_message += f" The profile '{aws_profile}' was not found in the AWS config file in {config.get_path()}."
67
+
68
+ # check for the existence of the profile and the path to the profile
69
+
70
+ raise RuntimeError(error_message) from e
@@ -22,7 +22,7 @@ logger = Logger()
22
22
 
23
23
 
24
24
  class SerializableModel:
25
- """Library to Serialize object to a DynamoDB Format"""
25
+ """Library to Serialize object to a DynamoDB Format or other dictionary"""
26
26
 
27
27
  T = TypeVar("T", bound="SerializableModel")
28
28
 
@@ -47,9 +47,16 @@ class SerializableModel:
47
47
 
48
48
  return mapped
49
49
 
50
+ def dict(self) -> Dict[str, Any]:
51
+ """
52
+ Same as .to_dictionary
53
+
54
+ """
55
+ return self.to_dictionary()
56
+
50
57
  def to_dictionary(self) -> Dict[str, Any]:
51
58
  """
52
- Convert the object to a dictionary.
59
+ Convert the object to a dictionary. Same as .dict()
53
60
  """
54
61
  # return Serialization.convert_object_to_dict(self)
55
62
  return Serialization.to_dict(
@@ -0,0 +1 @@
1
+ __version__ = '0.17.0'
@@ -0,0 +1,81 @@
1
+ import unittest
2
+ from pathlib import Path
3
+
4
+
5
+ class AWSConfigTest(unittest.TestCase):
6
+
7
+ def setUp(self):
8
+ config_path_dir = (
9
+ Path(__file__).parent.joinpath(".outputs", "aws_config").resolve()
10
+ )
11
+
12
+ if not config_path_dir.exists():
13
+ config_path_dir.mkdir(parents=True, exist_ok=True)
14
+
15
+ self.config_path = config_path_dir.joinpath("config")
16
+
17
+ def test_import(self):
18
+ from boto3_assist.aws_config import AWSConfig
19
+
20
+ self.assertTrue(AWSConfig)
21
+
22
+ def test_init(self):
23
+ from boto3_assist.aws_config import AWSConfig
24
+ import os
25
+
26
+ aws_config = AWSConfig()
27
+ self.assertTrue(aws_config)
28
+
29
+ def test_path(self):
30
+ from boto3_assist.aws_config import AWSConfig
31
+ import os
32
+
33
+ aws_config = AWSConfig()
34
+ path = aws_config.get_path()
35
+
36
+ self.assertTrue(os.path.exists(path))
37
+
38
+ def test_config_upsert_profile(self):
39
+ from boto3_assist.aws_config import AWSConfig
40
+ from boto3_assist.aws_config import AWSConfigProfile
41
+
42
+ aws_config = AWSConfig()
43
+ profile = AWSConfigProfile("us-east-2", "json")
44
+ profile.aws_access_key_id = "111111111111"
45
+ profile.aws_secret_access_key = "22222222222"
46
+ aws_config.upsert_profile("unit-test-profile", profile)
47
+
48
+ def test_config_upsert_sso(self):
49
+ from boto3_assist.aws_config import AWSConfig
50
+ from boto3_assist.aws_config import AWSConfigSSOSession
51
+
52
+ aws_config = AWSConfig()
53
+ sso = AWSConfigSSOSession("us-east-1", "json")
54
+ sso.sso_start_url = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
55
+ sso.sso_region = "us-east-2"
56
+ sso.sso_registration_scopes = "scopes"
57
+ aws_config.upsert_sso_session("unit-test-sso", sso)
58
+
59
+ def test_config_upsert_sso_with_profile(self):
60
+ from boto3_assist.aws_config import AWSConfig
61
+ from boto3_assist.aws_config import AWSConfigSSOSession
62
+ from boto3_assist.aws_config import AWSConfigProfile
63
+
64
+ aws_config = AWSConfig()
65
+ sso = AWSConfigSSOSession("us-east-1", "json")
66
+ sso.sso_start_url = "https://<account-domain>.awsapps.com/start/#/?tab=accounts"
67
+ sso.sso_region = "us-east-1"
68
+ sso.sso_registration_scopes = "sso:account:access"
69
+
70
+ section_name = "test-tenant-001"
71
+
72
+ profile = AWSConfigProfile("us-east-1", "json")
73
+ profile.sso_session = section_name
74
+ profile.sso_account_id = "AAAAAAAAAAAAAAAAAA"
75
+ profile.sso_role_name = "SomeRoleName"
76
+ profile.region = "us-east-1"
77
+ profile.output = "json"
78
+
79
+ aws_config.upsert_sso_session(
80
+ section_name, sso_session=sso, profile=profile, config_path=self.config_path
81
+ )
@@ -15,7 +15,7 @@ from boto3_assist.dynamodb.dynamodb_model_base import (
15
15
  class BaseCMSDBModel(DynamoDBModelBase):
16
16
  """
17
17
  The Base DB Model
18
- Sets a common set of proporrties for all models
18
+ Sets a common set of properties for all models
19
19
  """
20
20
 
21
21
  def __init__(self) -> None:
@@ -7,7 +7,7 @@ MIT License. See Project Root for the license information.
7
7
  import datetime as dt
8
8
  from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
9
9
  from boto3_assist.utilities.string_utility import StringUtility
10
- from tests.unit.dynamodb_tests.models.cms.base import BaseCMSDBModel
10
+ from tests.unit.dynamodb_tests.db_models.cms.base import BaseCMSDBModel
11
11
 
12
12
 
13
13
  class ContentBlock(BaseCMSDBModel):
@@ -41,13 +41,13 @@ class ContentBlock(BaseCMSDBModel):
41
41
  self.__setup_indexes()
42
42
 
43
43
  def __setup_indexes(self):
44
- primay: DynamoDBIndex = DynamoDBIndex()
45
- primay.name = "primary"
46
- primay.partition_key.attribute_name = "pk"
47
- primay.partition_key.value = lambda: DynamoDBKey.build_key(
44
+ primary: DynamoDBIndex = DynamoDBIndex()
45
+ primary.name = "primary"
46
+ primary.partition_key.attribute_name = "pk"
47
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(
48
48
  ("site", self.site_id), ("block-type", self.block_type)
49
49
  )
50
50
 
51
- primay.sort_key.attribute_name = "sk"
52
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("content", self.id))
53
- self.indexes.add_primary(primay)
51
+ primary.sort_key.attribute_name = "sk"
52
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("content", self.id))
53
+ self.indexes.add_primary(primary)
@@ -7,7 +7,7 @@ MIT License. See Project Root for the license information.
7
7
  from typing import List, Dict, Any
8
8
  import datetime as dt
9
9
  from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
10
- from tests.unit.dynamodb_tests.dbmodels.cms.base import BaseCMSDBModel
10
+ from tests.unit.dynamodb_tests.db_models.cms.base import BaseCMSDBModel
11
11
 
12
12
 
13
13
  class Page(BaseCMSDBModel):
@@ -45,16 +45,16 @@ class Page(BaseCMSDBModel):
45
45
  self.__setup_indexes()
46
46
 
47
47
  def __setup_indexes(self):
48
- primay: DynamoDBIndex = DynamoDBIndex()
49
- primay.name = "primary"
50
- primay.partition_key.attribute_name = "pk"
51
- primay.partition_key.value = lambda: DynamoDBKey.build_key(
48
+ primary: DynamoDBIndex = DynamoDBIndex()
49
+ primary.name = "primary"
50
+ primary.partition_key.attribute_name = "pk"
51
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(
52
52
  ("site", self.site_id), ("pages", None)
53
53
  )
54
54
 
55
- primay.sort_key.attribute_name = "sk"
56
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("page", self.slug))
57
- self.indexes.add_primary(primay)
55
+ primary.sort_key.attribute_name = "sk"
56
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("page", self.slug))
57
+ self.indexes.add_primary(primary)
58
58
 
59
59
  gsi1: DynamoDBIndex = DynamoDBIndex()
60
60
  gsi1.name = "gsi1"
@@ -69,7 +69,7 @@ class Page(BaseCMSDBModel):
69
69
  @property
70
70
  def id(self) -> str:
71
71
  """The id for the page"""
72
- return f"{self.site_id}/{self.slug}"
72
+ return f"{self.site_id}/{self.type}/{self.slug}"
73
73
 
74
74
  @id.setter
75
75
  def id(self, id: str):
@@ -6,7 +6,7 @@ MIT License. See Project Root for the license information.
6
6
 
7
7
  from typing import List
8
8
  from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
9
- from tests.unit.dynamodb_tests.dbmodels.cms.base import BaseCMSDBModel
9
+ from tests.unit.dynamodb_tests.db_models.cms.base import BaseCMSDBModel
10
10
 
11
11
 
12
12
  class Template(BaseCMSDBModel):
@@ -28,16 +28,16 @@ class Template(BaseCMSDBModel):
28
28
  self.__setup_indexes()
29
29
 
30
30
  def __setup_indexes(self):
31
- primay: DynamoDBIndex = DynamoDBIndex()
32
- primay.name = "primary"
33
- primay.partition_key.attribute_name = "pk"
34
- primay.partition_key.value = lambda: DynamoDBKey.build_key(
31
+ primary: DynamoDBIndex = DynamoDBIndex()
32
+ primary.name = "primary"
33
+ primary.partition_key.attribute_name = "pk"
34
+ primary.partition_key.value = lambda: DynamoDBKey.build_key(
35
35
  ("site", self.site_id), ("templates", None)
36
36
  )
37
37
 
38
- primay.sort_key.attribute_name = "sk"
39
- primay.sort_key.value = lambda: DynamoDBKey.build_key(("template", self.title))
40
- self.indexes.add_primary(primay)
38
+ primary.sort_key.attribute_name = "sk"
39
+ primary.sort_key.value = lambda: DynamoDBKey.build_key(("template", self.title))
40
+ self.indexes.add_primary(primary)
41
41
 
42
42
  gsi1: DynamoDBIndex = DynamoDBIndex()
43
43
  gsi1.name = "gsi1"
@@ -52,8 +52,10 @@ class Template(BaseCMSDBModel):
52
52
  @property
53
53
  def s3_object_key(self) -> str:
54
54
  """The s3 object key for the template"""
55
- return f"{self.site_id}/{self.title}"
55
+ return f"{self.site_id}/template/{self.type}/{self.title}"
56
56
 
57
57
  @s3_object_key.setter
58
58
  def s3_object_key(self, value: str):
59
+ # we don't set this field, but we also don't
60
+ # want an error during the serialization process
59
61
  pass