boto3-assist 0.11.0__tar.gz → 0.12.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 (136) hide show
  1. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/PKG-INFO +1 -1
  2. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/pyproject.toml +5 -1
  3. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/boto3session.py +23 -13
  4. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/connection.py +3 -1
  5. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb.py +2 -4
  6. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +2 -0
  7. boto3_assist-0.12.0/src/boto3_assist/version.py +1 -0
  8. boto3_assist-0.12.0/tests/integration/cross_account_connection_test.py +78 -0
  9. boto3_assist-0.12.0/tests/integration/tenant.py +185 -0
  10. boto3_assist-0.12.0/tests/integration/tenant_services.py +48 -0
  11. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/cms/content_block.py +1 -1
  12. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/cms/page.py +1 -1
  13. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/cms/template.py +1 -1
  14. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dynamodb_model_base_test.py +1 -1
  15. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dynamodb_model_projections_test.py +2 -2
  16. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dynamodb_model_serializtion_test.py +2 -2
  17. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dynamodb_moto_sorting_test.py +1 -1
  18. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/serializable_model_person_test.py +1 -3
  19. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/serializable_model_user_test.py +1 -1
  20. boto3_assist-0.11.0/src/boto3_assist/version.py +0 -1
  21. boto3_assist-0.11.0/tests/__top/__init__.py +0 -25
  22. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.env.docker +0 -0
  23. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.env.docker.001 +0 -0
  24. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.env.docker.nosql.workbench +0 -0
  25. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.env.unittest +0 -0
  26. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.gitignore +0 -0
  27. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.vscode/launch.json +0 -0
  28. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.vscode/settings.json +0 -0
  29. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/.vscode/tasks.json +0 -0
  30. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/LICENSE-EXPLAINED.txt +0 -0
  31. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/LICENSE.txt +0 -0
  32. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/README.md +0 -0
  33. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/aws_regions_with_status.csv +0 -0
  34. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/aws_regions_with_status.json +0 -0
  35. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/devops/build.py +0 -0
  36. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/devops/readme.md +0 -0
  37. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/__init__.py +0 -0
  38. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/cloudwatch/log_report.py +0 -0
  39. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/models/order_item_model.py +0 -0
  40. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/models/order_model.py +0 -0
  41. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/models/product_model.py +0 -0
  42. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/models/user_model.py +0 -0
  43. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/models/user_post_model.py +0 -0
  44. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/order_example/main.py +0 -0
  45. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/order_example/products.json +0 -0
  46. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/order_item_service.py +0 -0
  47. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/order_service.py +0 -0
  48. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/product_service.py +0 -0
  49. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/table_service.py +0 -0
  50. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/user_post_service.py +0 -0
  51. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/user_service.py +0 -0
  52. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
  53. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
  54. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/dynamodb/user_post_example/main.py +0 -0
  55. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/examples/ec2/regions_report.py +0 -0
  56. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/module-headers.txt +0 -0
  57. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/mypy.ini +0 -0
  58. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/requirements-dev.txt +0 -0
  59. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/requirements.txt +0 -0
  60. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/run-checks.sh +0 -0
  61. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/run_unit_tests.sh +0 -0
  62. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/__init__.py +0 -0
  63. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
  64. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
  65. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
  66. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
  67. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
  68. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
  69. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
  70. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
  71. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
  72. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
  73. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
  74. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/cognito/user.py +0 -0
  75. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/connection_tracker.py +0 -0
  76. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
  77. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
  78. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
  79. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
  80. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
  81. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +0 -0
  82. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
  83. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
  84. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
  85. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
  86. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/readme.md +0 -0
  87. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
  88. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
  89. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/environment_services/__init__.py +0 -0
  90. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
  91. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
  92. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
  93. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/http_status_codes.py +0 -0
  94. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/models/serializable_model.py +0 -0
  95. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/s3/s3.py +0 -0
  96. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
  97. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/s3/s3_connection.py +0 -0
  98. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
  99. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/s3/s3_object.py +0 -0
  100. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
  101. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
  102. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/ssm/connection.py +0 -0
  103. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
  104. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
  105. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
  106. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/file_operations.py +0 -0
  107. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/http_utility.py +0 -0
  108. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
  109. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
  110. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/serialization_utility.py +0 -0
  111. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/src/boto3_assist/utilities/string_utility.py +0 -0
  112. {boto3_assist-0.11.0 → boto3_assist-0.12.0}/tests/__init__.py +0 -0
  113. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/__init__.py +0 -0
  114. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/cms/base.py +0 -0
  115. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/simple_model.py +0 -0
  116. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/user_model.py +0 -0
  117. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dbmodels/user_required_fields_model.py +0 -0
  118. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/dynamodb_tests/dynamodb_reindex_test.py +0 -0
  119. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/examples_test/__init__.py +0 -0
  120. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/examples_test/user_service_test.py +0 -0
  121. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/lambda_tests/__init__.py +0 -0
  122. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/lambda_tests/event_info_test.py +0 -0
  123. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/__init__.py +0 -0
  124. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/models/person.py +0 -0
  125. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/models/user.py +0 -0
  126. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/models_tests/serializable_model_wide_test.py +0 -0
  127. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/parameter_store/__init__.py +0 -0
  128. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/parameter_store/parameter_store_test.py +0 -0
  129. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/s3/__init__.py +0 -0
  130. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/s3/files/test.txt +0 -0
  131. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/s3/s3_event_data_test.py +0 -0
  132. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/s3/s3_file_delete_test.py +0 -0
  133. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/s3/s3_file_upload_test.py +0 -0
  134. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/utilities/__init__.py +0 -0
  135. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/utilities/serialization_utility_test.py +0 -0
  136. {boto3_assist-0.11.0/tests → boto3_assist-0.12.0/tests/unit}/utilities/string_utility_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -8,10 +8,14 @@ packages = ["src/boto3_assist"]
8
8
  [tool.pytest.ini_options]
9
9
  pythonpath = ["src"]
10
10
  testpaths = ["tests"]
11
+ markers = [
12
+ "integration: marks tests as integration (deselect with '-m \"not integration\"')"
13
+ ]
14
+ addopts = "-m 'not integration'"
11
15
 
12
16
  [project]
13
17
  name = "boto3_assist"
14
- version = "0.11.0"
18
+ version = "0.12.0"
15
19
 
16
20
  authors = [
17
21
  { name="Eric Wilson", email="boto3-assist@geekcafe.com" }
@@ -27,7 +27,7 @@ class Boto3SessionManager:
27
27
  aws_region: Optional[str] = None,
28
28
  assume_role_arn: Optional[str] = None,
29
29
  assume_role_session_name: Optional[str] = None,
30
- cross_account_role_arn: Optional[str] = None,
30
+ # cross_account_role_arn: Optional[str] = None,
31
31
  config: Optional[Config] = None,
32
32
  aws_endpoint_url: Optional[str] = None,
33
33
  aws_access_key_id: Optional[str] = None,
@@ -40,7 +40,7 @@ class Boto3SessionManager:
40
40
  self.assume_role_arn = assume_role_arn
41
41
  self.assume_role_session_name = assume_role_session_name
42
42
  self.config = config
43
- self.cross_account_role_arn = cross_account_role_arn
43
+ # # self.cross_account_role_arn = cross_account_role_arn
44
44
  self.endpoint_url = aws_endpoint_url
45
45
  self.aws_access_key_id = aws_access_key_id
46
46
  self.aws_secret_access_key = aws_secret_access_key
@@ -57,36 +57,46 @@ class Boto3SessionManager:
57
57
 
58
58
  profile = self.aws_profile or EnvironmentVariables.AWS.profile()
59
59
  region = self.aws_region or EnvironmentVariables.AWS.region()
60
- if self.assume_role_arn:
61
- self.__assume_role()
62
- else:
63
- logger.debug("Connecting without assuming a role.")
64
- self.__session = self.__get_aws_session(profile, region)
60
+
61
+ logger.debug("Connecting without assuming a role.")
62
+ self.__session = self.__get_aws_session(profile, region)
65
63
 
66
64
  if profile:
67
65
  print(f"Connecting with a profile: {profile}")
68
66
 
67
+ if self.assume_role_arn:
68
+ self.__assume_role()
69
+
69
70
  def __assume_role(self):
70
71
  """Assume an AWS IAM role."""
71
72
  try:
73
+ if not self.__session:
74
+ raise RuntimeError(
75
+ "Session must be established before assuming a role."
76
+ )
77
+
72
78
  logger.debug(f"Assuming role {self.assume_role_arn}")
73
- sts_client = boto3.client("sts")
79
+
80
+ sts_client = self.__session.client("sts")
74
81
  session_name = (
75
82
  self.assume_role_session_name
76
83
  or f"AssumeRoleSessionFor{self.service_name}"
77
84
  )
78
- if not self.assume_role_arn:
79
- raise ValueError("assume_role_arn is required")
85
+
80
86
  assumed_role_response = sts_client.assume_role(
81
87
  RoleArn=self.assume_role_arn,
82
88
  RoleSessionName=session_name,
83
89
  )
84
90
  credentials = assumed_role_response["Credentials"]
91
+
92
+ # Now override the session with assumed credentials
85
93
  self.__session = boto3.Session(
86
94
  aws_access_key_id=credentials["AccessKeyId"],
87
95
  aws_secret_access_key=credentials["SecretAccessKey"],
88
96
  aws_session_token=credentials["SessionToken"],
97
+ region_name=self.aws_region,
89
98
  )
99
+ logger.debug("Successfully assumed role and created new session.")
90
100
 
91
101
  except Exception as e:
92
102
  logger.error(f"Error assuming role: {e}")
@@ -117,9 +127,9 @@ class Boto3SessionManager:
117
127
  "region": self.aws_region,
118
128
  "aws_access_key_id": tmp_access_key_id,
119
129
  "aws_secret_access_key": tmp_secret_access_key,
120
- "aws_session_token": "*******"
121
- if self.aws_session_token is not None
122
- else "",
130
+ "aws_session_token": (
131
+ "*******" if self.aws_session_token is not None else ""
132
+ ),
123
133
  }
124
134
  )
125
135
  logger.debug("Creating boto3 session")
@@ -30,6 +30,7 @@ class Connection:
30
30
  aws_access_key_id: Optional[str] = None,
31
31
  aws_secret_access_key: Optional[str] = None,
32
32
  aws_end_point_url: Optional[str] = None,
33
+ assume_role_arn: Optional[str] = None,
33
34
  ) -> None:
34
35
  self.__aws_profile = aws_profile
35
36
  self.__aws_region = aws_region
@@ -37,7 +38,7 @@ class Connection:
37
38
  self.__aws_secret_access_key = aws_secret_access_key
38
39
  self.end_point_url = aws_end_point_url
39
40
  self.__session: Boto3SessionManager | None = None
40
-
41
+ self.__assume_role_arn: Optional[str] = assume_role_arn
41
42
  self.__service_name: str | None = service_name
42
43
 
43
44
  if self.__service_name is None:
@@ -73,6 +74,7 @@ class Connection:
73
74
  aws_access_key_id=self.aws_access_key_id,
74
75
  aws_secret_access_key=self.aws_secret_access_key,
75
76
  aws_endpoint_url=self.end_point_url,
77
+ assume_role_arn=self.__assume_role_arn,
76
78
  )
77
79
 
78
80
  tracker.add(service_name=self.service_name)
@@ -24,7 +24,6 @@ from boto3_assist.utilities.string_utility import StringUtility
24
24
  logger = Logger()
25
25
 
26
26
 
27
-
28
27
  class DynamoDB(DynamoDBConnection):
29
28
  """
30
29
  DynamoDB. Wrapper for basic DynamoDB Connection and Actions
@@ -41,6 +40,7 @@ class DynamoDB(DynamoDBConnection):
41
40
  aws_end_point_url: Optional[str] = None,
42
41
  aws_access_key_id: Optional[str] = None,
43
42
  aws_secret_access_key: Optional[str] = None,
43
+ assume_role_arn: Optional[str] = None,
44
44
  ) -> None:
45
45
  super().__init__(
46
46
  aws_profile=aws_profile,
@@ -48,6 +48,7 @@ class DynamoDB(DynamoDBConnection):
48
48
  aws_end_point_url=aws_end_point_url,
49
49
  aws_access_key_id=aws_access_key_id,
50
50
  aws_secret_access_key=aws_secret_access_key,
51
+ assume_role_arn=assume_role_arn,
51
52
  )
52
53
  self.helpers: DynamoDBHelpers = DynamoDBHelpers()
53
54
  self.log_dynamodb_item_size = (
@@ -55,7 +56,6 @@ class DynamoDB(DynamoDBConnection):
55
56
  )
56
57
  logger.setLevel(os.getenv("LOG_LEVEL", "INFO"))
57
58
 
58
-
59
59
  def save(
60
60
  self,
61
61
  item: dict | DynamoDBModelBase,
@@ -169,7 +169,6 @@ class DynamoDB(DynamoDBConnection):
169
169
  call_type: str = "resource",
170
170
  ) -> Dict[str, Any]: ...
171
171
 
172
-
173
172
  def get(
174
173
  self,
175
174
  key: Optional[dict] = None,
@@ -344,7 +343,6 @@ class DynamoDB(DynamoDBConnection):
344
343
  ) -> dict:
345
344
  pass
346
345
 
347
-
348
346
  def delete(
349
347
  self,
350
348
  *,
@@ -32,6 +32,7 @@ class DynamoDBConnection(Connection):
32
32
  aws_end_point_url: Optional[str] = None,
33
33
  aws_access_key_id: Optional[str] = None,
34
34
  aws_secret_access_key: Optional[str] = None,
35
+ assume_role_arn: Optional[str] = None,
35
36
  ) -> None:
36
37
  super().__init__(
37
38
  service_name="dynamodb",
@@ -40,6 +41,7 @@ class DynamoDBConnection(Connection):
40
41
  aws_access_key_id=aws_access_key_id,
41
42
  aws_secret_access_key=aws_secret_access_key,
42
43
  aws_end_point_url=aws_end_point_url,
44
+ assume_role_arn=assume_role_arn,
43
45
  )
44
46
 
45
47
  self.__dynamodb_client: DynamoDBClient | None = None
@@ -0,0 +1 @@
1
+ __version__ = '0.12.0'
@@ -0,0 +1,78 @@
1
+ import pytest
2
+ import os
3
+ from typing import List
4
+ from boto3_assist.dynamodb.dynamodb import DynamoDB
5
+ from tests.integration.tenant_services import TenantServices
6
+
7
+
8
+ @pytest.mark.integration
9
+ def test_cross_account_role_assumption_with_profile():
10
+ responses = []
11
+
12
+ profile_name = os.getenv("AWS_PROFILE")
13
+
14
+ connections: List[dict] = [
15
+ {
16
+ "profile_name": profile_name,
17
+ "aws_account": "959096737760",
18
+ "aws_region": "us-east-1",
19
+ "role_name": "CrossAccountAccessRole",
20
+ "table_name": "db-us-east-1",
21
+ "enabled": False,
22
+ },
23
+ {
24
+ "profile_name": profile_name,
25
+ "aws_account": "959096737760",
26
+ "aws_region": "eu-west-2",
27
+ "role_name": "CrossAccountAccessRole",
28
+ "table_name": "db-eu-west-2",
29
+ "enabled": False,
30
+ },
31
+ {
32
+ "profile_name": profile_name,
33
+ "aws_account": "257932641017",
34
+ "aws_region": "us-east-1",
35
+ "role_name": "CrossAccountAccessRole",
36
+ "table_name": "aplos-nca-saas-production-demo-001-database",
37
+ "enabled": True,
38
+ },
39
+ {
40
+ "profile_name": profile_name,
41
+ "aws_region": "us-east-1",
42
+ "aws_account": "211125601483",
43
+ "role_name": "CrossAccountAccessRole",
44
+ "table_name": "aplos-nca-saas-production-app-database",
45
+ "enabled": True,
46
+ },
47
+ ]
48
+
49
+ for connection in connections:
50
+ role_arn = (
51
+ f"arn:aws:iam::{connection['aws_account']}:role/{connection['role_name']}"
52
+ )
53
+ if connection["enabled"]:
54
+ db = DynamoDB(
55
+ aws_profile=connection["profile_name"],
56
+ aws_region=connection["aws_region"],
57
+ assume_role_arn=role_arn,
58
+ )
59
+ ts: TenantServices = TenantServices(
60
+ db=db, table_name=connection["table_name"]
61
+ )
62
+ response = ts.list()
63
+ responses.append(response)
64
+ # print(response)
65
+ else:
66
+ responses.append(None)
67
+
68
+ print(len(responses))
69
+ assert len(responses) == 3
70
+
71
+
72
+ def main():
73
+ test_cross_account_role_assumption_with_profile()
74
+ pass
75
+
76
+
77
+ if __name__ == "__main__":
78
+ main()
@@ -0,0 +1,185 @@
1
+ """
2
+ Tenant Model
3
+ """
4
+
5
+ import datetime
6
+ from typing import Optional, Literal
7
+ from boto3_assist.dynamodb.dynamodb_index import (
8
+ DynamoDBIndex,
9
+ DynamoDBKey,
10
+ )
11
+ from boto3_assist.dynamodb.dynamodb_model_base import (
12
+ DynamoDBModelBase,
13
+ exclude_from_serialization,
14
+ )
15
+
16
+ from boto3_assist.utilities.datetime_utility import DatetimeUtility
17
+
18
+
19
+ class Tenant(DynamoDBModelBase):
20
+ """Database Model for the Tenant Entity"""
21
+
22
+ def __init__(
23
+ self,
24
+ id: Optional[str] = None, # pylint: disable=w0622
25
+ ) -> None:
26
+ super().__init__()
27
+ self.id: Optional[str] = id
28
+ self.name: Optional[str] = None
29
+ self.email: Optional[str] = None
30
+ self.subscription_id: Optional[str] = None
31
+ self.type: Optional[str] = None
32
+ self.__status: Optional[str] = None
33
+ self.status_message: str = ""
34
+ self.company_name: Optional[str] = None
35
+ self.created_utc: datetime.datetime = DatetimeUtility.get_utc_now()
36
+ self.modified_utc: datetime.datetime = DatetimeUtility.get_utc_now()
37
+ self.reindexed_utc: Optional[datetime.datetime] = None
38
+ self.__onboard_utc: Optional[datetime.datetime] = None
39
+ self.__setup_indexes()
40
+
41
+ def __setup_indexes(self):
42
+ self.indexes.add_primary(
43
+ DynamoDBIndex(
44
+ index_name="primary",
45
+ partition_key=DynamoDBKey(
46
+ attribute_name="pk",
47
+ value=lambda: f"tenant#{self.id if self.id else ''}",
48
+ ),
49
+ sort_key=DynamoDBKey(
50
+ attribute_name="sk",
51
+ value=lambda: f"tenant#{self.id if self.id else ''}",
52
+ ),
53
+ )
54
+ )
55
+
56
+ self.indexes.add_secondary(
57
+ DynamoDBIndex(
58
+ index_name="gsi0",
59
+ partition_key=DynamoDBKey(
60
+ attribute_name="gsi0_pk",
61
+ value="tenants#",
62
+ ),
63
+ sort_key=DynamoDBKey(
64
+ attribute_name="gsi0_sk",
65
+ value=lambda: f"name#{self.__sort_name if self.__sort_name else ''}",
66
+ ),
67
+ )
68
+ )
69
+
70
+ self.indexes.add_secondary(
71
+ DynamoDBIndex(
72
+ index_name="gsi1",
73
+ partition_key=DynamoDBKey(
74
+ attribute_name="gsi1_pk",
75
+ value="tenants#",
76
+ ),
77
+ sort_key=DynamoDBKey(
78
+ attribute_name="gsi1_sk",
79
+ value=lambda: (
80
+ f"status#{self.status if self.status else ''}"
81
+ f"name#{self.__sort_name if self.__sort_name else ''}"
82
+ ),
83
+ ),
84
+ )
85
+ )
86
+
87
+ self.indexes.add_secondary(
88
+ DynamoDBIndex(
89
+ index_name="gsi2",
90
+ partition_key=DynamoDBKey(
91
+ attribute_name="gsi2_pk",
92
+ value="tenants#",
93
+ ),
94
+ sort_key=DynamoDBKey(
95
+ attribute_name="gsi2_sk",
96
+ value=lambda: f"onboard-ts#{self.onboard_utc.timestamp() if self.onboard_utc else ''}",
97
+ ),
98
+ )
99
+ )
100
+
101
+ @property
102
+ @exclude_from_serialization
103
+ def modifed_date(self) -> Optional[str]:
104
+ """Backward compatibale db model for modified date"""
105
+ return str(self.modified_utc)
106
+
107
+ @modifed_date.setter
108
+ def modifed_date(self, value: Optional[str]) -> None:
109
+ v = DatetimeUtility.to_datetime_utc(value=value)
110
+
111
+ self.modified_utc = v or DatetimeUtility.get_utc_now()
112
+
113
+ @property
114
+ @exclude_from_serialization
115
+ def onboarding_date(self) -> Optional[str]:
116
+ """Backward compatibale db model for modified date"""
117
+ return str(self.onboard_utc)
118
+
119
+ @onboarding_date.setter
120
+ def onboarding_date(self, value: Optional[str]) -> None:
121
+ self.onboard_utc = DatetimeUtility.to_datetime_utc(value=value)
122
+
123
+ @property
124
+ @exclude_from_serialization
125
+ def email_address(self) -> Optional[str]:
126
+ """Backward compatibale db model for email address"""
127
+ return self.email
128
+
129
+ @email_address.setter
130
+ def email_address(self, value: Optional[str]) -> None:
131
+ self.email = value
132
+
133
+ @property
134
+ def onboard_utc(self) -> Optional[datetime.datetime]:
135
+ """The UTC date and time the user was onboarded"""
136
+ return DatetimeUtility.to_datetime_utc(self.__onboard_utc)
137
+
138
+ @onboard_utc.setter
139
+ def onboard_utc(self, value: Optional[datetime.datetime]) -> None:
140
+ self.__onboard_utc = DatetimeUtility.to_datetime_utc(
141
+ value=value, default=DatetimeUtility.get_utc_now()
142
+ )
143
+
144
+ @property
145
+ def status(self) -> Optional[Literal["enabled", "disabled", "locked"]]:
146
+ """The status of the tenant"""
147
+
148
+ return self.__status
149
+
150
+ @status.setter
151
+ def status(self, value: Optional[Literal["enabled", "disabled", "locked"]]) -> None:
152
+ if value is not None:
153
+ value = str(value).lower()
154
+
155
+ self.__status = value
156
+
157
+ @property
158
+ def __sort_name(self) -> Optional[str]:
159
+ if self.name is None:
160
+ return None
161
+ else:
162
+ return self.name.lower()
163
+
164
+ @property
165
+ def record_type(self) -> str:
166
+ """
167
+ The type of record we are storing to help load the
168
+ correct object at runtime if needed.
169
+ """
170
+ name = __name__.rsplit(".", maxsplit=1)[-1]
171
+ return name
172
+
173
+ @record_type.setter
174
+ def record_type(self, value: str) -> None:
175
+ pass
176
+
177
+ @property
178
+ def subscription(self) -> dict | None:
179
+ """The tenants current subscription - if bound to one"""
180
+ return self.__subscription
181
+
182
+ @subscription.setter
183
+ def subscription(self, value: dict) -> None:
184
+
185
+ self.__subscription = value
@@ -0,0 +1,48 @@
1
+ from typing import Optional
2
+ from .tenant import Tenant
3
+ from boto3_assist.dynamodb.dynamodb import DynamoDB
4
+
5
+
6
+ class TenantServices:
7
+
8
+ def __init__(self, db: DynamoDB, table_name: str) -> None:
9
+ self.db: DynamoDB = db or DynamoDB()
10
+ self.table_name: str = table_name
11
+
12
+ def list(
13
+ self,
14
+ *,
15
+ status: Optional[str] = None,
16
+ ascending: bool = True,
17
+ do_projections: bool = False,
18
+ strongly_consistent: bool = False,
19
+ start_key: Optional[dict] = None,
20
+ ) -> dict:
21
+ """
22
+ List all Tenants within an optional status (enabled or )
23
+
24
+ Args:
25
+ start_key (Optional[str]): A start key for paged results.
26
+ do_projections (bool, optional): Determines if we do projections or not. Defaults to False.
27
+
28
+ Returns:
29
+ dict: DynamoDB response dictionary
30
+ """
31
+ model: Tenant = Tenant()
32
+ index_name: str = "gsi0"
33
+ if status:
34
+ model.status = status
35
+ index_name = "gsi1"
36
+ key = model.get_key(index_name).key()
37
+ response = self.db.query_by_criteria(
38
+ model=model,
39
+ index_name=index_name,
40
+ key=key,
41
+ start_key=start_key,
42
+ do_projections=do_projections,
43
+ table_name=self.table_name,
44
+ strongly_consistent=strongly_consistent,
45
+ ascending=ascending,
46
+ )
47
+
48
+ return response
@@ -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.dynamodb_tests.models.cms.base import BaseCMSDBModel
10
+ from tests.unit.dynamodb_tests.models.cms.base import BaseCMSDBModel
11
11
 
12
12
 
13
13
  class ContentBlock(BaseCMSDBModel):
@@ -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.dynamodb_tests.dbmodels.cms.base import BaseCMSDBModel
10
+ from tests.unit.dynamodb_tests.dbmodels.cms.base import BaseCMSDBModel
11
11
 
12
12
 
13
13
  class Page(BaseCMSDBModel):
@@ -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.dynamodb_tests.models.cms.base import BaseCMSDBModel
9
+ from tests.unit.dynamodb_tests.dbmodels.cms.base import BaseCMSDBModel
10
10
 
11
11
 
12
12
  class Template(BaseCMSDBModel):
@@ -9,7 +9,7 @@ from typing import Dict, List
9
9
 
10
10
  from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex
11
11
  from boto3_assist.dynamodb.dynamodb_key import DynamoDBKey
12
- from tests.dynamodb_tests.dbmodels.user_model import User
12
+ from tests.unit.dynamodb_tests.dbmodels.user_model import User
13
13
 
14
14
 
15
15
  class DynamoDBModelUnitTest(unittest.TestCase):
@@ -7,8 +7,8 @@ MIT License. See Project Root for the license information.
7
7
  import unittest
8
8
 
9
9
 
10
- from tests.dynamodb_tests.dbmodels.user_model import User
11
- from tests.dynamodb_tests.dbmodels.simple_model import Simple
10
+ from tests.unit.dynamodb_tests.dbmodels.user_model import User
11
+ from tests.unit.dynamodb_tests.dbmodels.simple_model import Simple
12
12
 
13
13
 
14
14
  class DynamoDBModeProjectionlUnitTest(unittest.TestCase):
@@ -7,8 +7,8 @@ MIT License. See Project Root for the license information.
7
7
  import unittest
8
8
 
9
9
 
10
- from tests.dynamodb_tests.dbmodels.user_model import User
11
- from tests.dynamodb_tests.dbmodels.user_required_fields_model import User as User2
10
+ from tests.unit.dynamodb_tests.dbmodels.user_model import User
11
+ from tests.unit.dynamodb_tests.dbmodels.user_required_fields_model import User as User2
12
12
 
13
13
 
14
14
  class DynamoDBModelSerializationUnitTest(unittest.TestCase):
@@ -12,7 +12,7 @@ from mypy_boto3_dynamodb import DynamoDBClient
12
12
 
13
13
  from boto3_assist.dynamodb.dynamodb import DynamoDB
14
14
  from boto3_assist.environment_services.environment_loader import EnvironmentLoader
15
- from tests.dynamodb_tests.dbmodels.cms.page import Page
15
+ from tests.unit.dynamodb_tests.dbmodels.cms.page import Page
16
16
 
17
17
 
18
18
  @moto.mock_aws
@@ -7,9 +7,7 @@ MIT License. See Project Root for the license information.
7
7
  import unittest
8
8
  from typing import Dict, List
9
9
  from boto3_assist.models.serializable_model import SerializableModel
10
- from tests.models_tests.models.person import Person
11
-
12
-
10
+ from tests.unit.models_tests.models.person import Person
13
11
 
14
12
 
15
13
  class TestSerializableModel(unittest.TestCase):
@@ -7,7 +7,7 @@ MIT License. See Project Root for the license information.
7
7
  import unittest
8
8
  from typing import Dict, List, Any
9
9
  from boto3_assist.models.serializable_model import SerializableModel
10
- from tests.models_tests.models.user import User
10
+ from tests.unit.models_tests.models.user import User
11
11
 
12
12
 
13
13
  class TestSerializableModel(unittest.TestCase):
@@ -1 +0,0 @@
1
- __version__ = '0.11.0'
@@ -1,25 +0,0 @@
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 sys
9
- from pathlib import Path
10
-
11
- VERBOSE: bool = os.getenv("VERBOSE") or False
12
-
13
- if VERBOSE:
14
- print("👋 init test paths for __top")
15
-
16
-
17
- root_directory = Path(__file__).resolve().parent.parent.parent
18
- src_directory = os.path.join(root_directory, "src")
19
- # inject src path to python search path
20
- sys.path.insert(0, src_directory)
21
-
22
- if VERBOSE:
23
- print("")
24
- for p in sys.path:
25
- print(f"👉 {p}")
File without changes
File without changes
File without changes
File without changes
File without changes