clear-skies-aws 1.10.2__py3-none-any.whl → 2.0.2__py3-none-any.whl

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 (86) hide show
  1. {clear_skies_aws-1.10.2.dist-info → clear_skies_aws-2.0.2.dist-info}/METADATA +36 -35
  2. clear_skies_aws-2.0.2.dist-info/RECORD +63 -0
  3. {clear_skies_aws-1.10.2.dist-info → clear_skies_aws-2.0.2.dist-info}/WHEEL +1 -1
  4. clear_skies_aws-2.0.2.dist-info/licenses/LICENSE +21 -0
  5. clearskies_aws/__init__.py +15 -2
  6. clearskies_aws/actions/__init__.py +13 -106
  7. clearskies_aws/actions/action_aws.py +74 -57
  8. clearskies_aws/actions/assume_role.py +43 -30
  9. clearskies_aws/actions/ses.py +82 -73
  10. clearskies_aws/actions/sns.py +27 -30
  11. clearskies_aws/actions/sqs.py +32 -33
  12. clearskies_aws/actions/step_function.py +38 -31
  13. clearskies_aws/backends/__init__.py +11 -4
  14. clearskies_aws/backends/backend.py +106 -0
  15. clearskies_aws/backends/dynamo_db_backend.py +150 -155
  16. clearskies_aws/backends/dynamo_db_condition_parser.py +40 -80
  17. clearskies_aws/backends/dynamo_db_parti_ql_backend.py +179 -337
  18. clearskies_aws/backends/sqs_backend.py +32 -51
  19. clearskies_aws/configs/__init__.py +0 -0
  20. clearskies_aws/contexts/__init__.py +23 -10
  21. clearskies_aws/contexts/cli_web_socket_mock.py +19 -0
  22. clearskies_aws/contexts/lambda_alb.py +76 -0
  23. clearskies_aws/contexts/lambda_api_gateway.py +75 -28
  24. clearskies_aws/contexts/lambda_api_gateway_web_socket.py +56 -29
  25. clearskies_aws/contexts/lambda_invocation.py +15 -44
  26. clearskies_aws/contexts/lambda_sns.py +8 -33
  27. clearskies_aws/contexts/lambda_sqs_standard_partial_batch.py +14 -36
  28. clearskies_aws/di/__init__.py +6 -1
  29. clearskies_aws/di/aws_additional_config_auto_import.py +37 -0
  30. clearskies_aws/di/inject/__init__.py +6 -0
  31. clearskies_aws/di/inject/boto3.py +15 -0
  32. clearskies_aws/di/inject/boto3_session.py +13 -0
  33. clearskies_aws/di/inject/parameter_store.py +15 -0
  34. clearskies_aws/{handlers → endpoints}/secrets_manager_rotation.py +76 -55
  35. clearskies_aws/endpoints/simple_body_routing.py +41 -0
  36. clearskies_aws/input_outputs/__init__.py +21 -8
  37. clearskies_aws/input_outputs/{cli_websocket_mock.py → cli_web_socket_mock.py} +9 -3
  38. clearskies_aws/input_outputs/lambda_alb.py +53 -0
  39. clearskies_aws/input_outputs/lambda_api_gateway.py +106 -88
  40. clearskies_aws/input_outputs/lambda_api_gateway_web_socket.py +69 -6
  41. clearskies_aws/input_outputs/lambda_input_output.py +87 -0
  42. clearskies_aws/input_outputs/lambda_invocation.py +77 -26
  43. clearskies_aws/input_outputs/lambda_sns.py +66 -39
  44. clearskies_aws/input_outputs/lambda_sqs_standard.py +70 -40
  45. clearskies_aws/mocks/actions/ses.py +25 -19
  46. clearskies_aws/mocks/actions/sns.py +18 -12
  47. clearskies_aws/mocks/actions/sqs.py +18 -12
  48. clearskies_aws/mocks/actions/step_function.py +19 -13
  49. clearskies_aws/models/__init__.py +0 -0
  50. clearskies_aws/models/web_socket_connection_model.py +182 -0
  51. clearskies_aws/secrets/__init__.py +13 -7
  52. clearskies_aws/secrets/additional_configs/__init__.py +10 -2
  53. clearskies_aws/secrets/additional_configs/iam_db_auth.py +26 -16
  54. clearskies_aws/secrets/additional_configs/iam_db_auth_with_ssm.py +43 -39
  55. clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +30 -31
  56. clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssm_bastion.py +70 -49
  57. clearskies_aws/secrets/akeyless_with_ssm_cache.py +32 -18
  58. clearskies_aws/secrets/parameter_store.py +34 -32
  59. clearskies_aws/secrets/secrets.py +16 -0
  60. clearskies_aws/secrets/secrets_manager.py +78 -57
  61. clear_skies_aws-1.10.2.dist-info/LICENSE +0 -7
  62. clear_skies_aws-1.10.2.dist-info/RECORD +0 -71
  63. clearskies_aws/actions/assume_role_test.py +0 -72
  64. clearskies_aws/actions/ses_test.py +0 -89
  65. clearskies_aws/actions/sns_test.py +0 -77
  66. clearskies_aws/actions/sqs_test.py +0 -127
  67. clearskies_aws/actions/step_function_test.py +0 -103
  68. clearskies_aws/backends/dynamo_db_backend_test.py +0 -300
  69. clearskies_aws/backends/dynamo_db_condition_parser_test.py +0 -266
  70. clearskies_aws/backends/dynamo_db_parti_ql_backend_test.py +0 -544
  71. clearskies_aws/backends/sqs_backend_test.py +0 -31
  72. clearskies_aws/contexts/cli.py +0 -19
  73. clearskies_aws/contexts/cli_websocket_mock.py +0 -33
  74. clearskies_aws/contexts/lambda_elb.py +0 -30
  75. clearskies_aws/contexts/lambda_http_gateway.py +0 -30
  76. clearskies_aws/contexts/lambda_sqs_standard_partial_batch_test.py +0 -66
  77. clearskies_aws/contexts/wsgi.py +0 -19
  78. clearskies_aws/di/standard_dependencies.py +0 -60
  79. clearskies_aws/handlers/simple_body_routing.py +0 -39
  80. clearskies_aws/input_outputs/lambda_api_gateway_test.py +0 -87
  81. clearskies_aws/input_outputs/lambda_elb.py +0 -21
  82. clearskies_aws/input_outputs/lambda_http_gateway.py +0 -12
  83. clearskies_aws/secrets/parameter_store_test.py +0 -18
  84. clearskies_aws/secrets/secrets_manager_test.py +0 -18
  85. clearskies_aws/web_socket_connection_model.py +0 -43
  86. clearskies_aws/{handlers → endpoints}/__init__.py +1 -1
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import base64
2
4
  import json
3
5
  import logging
4
6
  from decimal import Decimal, DecimalException
5
- from typing import Any, Dict, List, Tuple
7
+ from typing import Any
6
8
 
7
- from clearskies import ConditionParser
9
+ from clearskies import Validator
8
10
 
9
11
  # Ensure AttributeValueTypeDef is imported from the correct boto3 types package
10
12
  # This is crucial for the "ideal fix".
@@ -13,7 +15,7 @@ from types_boto3_dynamodb.type_defs import AttributeValueTypeDef
13
15
  logger = logging.getLogger(__name__)
14
16
 
15
17
 
16
- class DynamoDBConditionParser(ConditionParser):
18
+ class DynamoDBConditionParser(Validator):
17
19
  """
18
20
  Parses string conditions into a structured format suitable for DynamoDB PartiQL queries.
19
21
 
@@ -22,7 +24,7 @@ class DynamoDBConditionParser(ConditionParser):
22
24
  to convert Python values into the DynamoDB AttributeValue format.
23
25
  """
24
26
 
25
- operator_lengths: Dict[str, int] = {
27
+ operator_lengths: dict[str, int] = {
26
28
  "<>": 2,
27
29
  "<=": 2,
28
30
  ">=": 2,
@@ -41,7 +43,7 @@ class DynamoDBConditionParser(ConditionParser):
41
43
  "contains": 9,
42
44
  "begins_with": 12,
43
45
  }
44
- operators: List[str] = [
46
+ operators: list[str] = [
45
47
  # Longer operators first to help with matching
46
48
  "is not null",
47
49
  "is not missing",
@@ -61,7 +63,7 @@ class DynamoDBConditionParser(ConditionParser):
61
63
  "=",
62
64
  "in",
63
65
  ]
64
- operators_for_matching: Dict[str, str] = {
66
+ operators_for_matching: dict[str, str] = {
65
67
  "like": " like ",
66
68
  "in": " in ",
67
69
  "is not missing": " is not missing",
@@ -73,7 +75,7 @@ class DynamoDBConditionParser(ConditionParser):
73
75
  "begins_with": " begins_with",
74
76
  "contains": " contains",
75
77
  }
76
- operators_with_simple_placeholders: Dict[str, bool] = {
78
+ operators_with_simple_placeholders: dict[str, bool] = {
77
79
  "<>": True,
78
80
  "<=": True,
79
81
  ">=": True,
@@ -88,17 +90,17 @@ class DynamoDBConditionParser(ConditionParser):
88
90
  "is not missing",
89
91
  "is missing",
90
92
  }
91
- operator_needs_remap: Dict[str, str] = {
93
+ operator_needs_remap: dict[str, str] = {
92
94
  "is not null": "is not missing",
93
95
  "is null": "is missing",
94
96
  }
95
97
  operators_with_special_placeholders: set[str] = {"begins_with", "contains"}
96
98
 
97
- def parse_condition(self, condition: str) -> Dict[str, Any]:
99
+ def parse_condition(self, condition: str) -> dict[str, Any]:
98
100
  """
99
- Parses a string condition into a structured dictionary.
101
+ Parse a string condition into a structured dictionary.
100
102
 
101
- The "values" key in the returned dictionary will contain List[AttributeValueTypeDef].
103
+ The "values" key in the returned dictionary will contain list[AttributeValueTypeDef].
102
104
 
103
105
  Args:
104
106
  condition: The condition string to parse.
@@ -114,9 +116,7 @@ class DynamoDBConditionParser(ConditionParser):
114
116
 
115
117
  for operator in self.operators:
116
118
  try:
117
- operator_for_match: str = self.operators_for_matching.get(
118
- operator, operator
119
- )
119
+ operator_for_match: str = self.operators_for_matching.get(operator, operator)
120
120
  index: int = lowercase_condition.index(operator_for_match)
121
121
 
122
122
  if matching_index == -1 or index < matching_index:
@@ -134,26 +134,21 @@ class DynamoDBConditionParser(ConditionParser):
134
134
  raise ValueError(f"No supported operators found in condition {condition}")
135
135
 
136
136
  column: str = condition[:matching_index].strip()
137
- value: str = condition[
138
- matching_index + self.operator_lengths[matching_operator] :
139
- ].strip()
137
+ value: str = condition[matching_index + self.operator_lengths[matching_operator] :].strip()
140
138
 
141
139
  if len(value) >= 2:
142
140
  first_char = value[0]
143
141
  last_char = value[-1]
144
- if (first_char == "'" and last_char == "'") or (
145
- first_char == '"' and last_char == '"'
146
- ):
142
+ if (first_char == "'" and last_char == "'") or (first_char == '"' and last_char == '"'):
147
143
  value = value[1:-1]
148
144
 
149
- raw_values: List[str] = []
145
+ raw_values: list[str] = []
150
146
 
151
147
  if matching_operator == "in":
152
148
  raw_values = self._parse_condition_list(value) if value else []
153
149
  elif matching_operator not in self.operators_without_placeholders and not (
154
150
  matching_operator in self.operator_needs_remap
155
- and self.operator_needs_remap[matching_operator]
156
- in self.operators_without_placeholders
151
+ and self.operator_needs_remap[matching_operator] in self.operators_without_placeholders
157
152
  ):
158
153
  raw_values = [value]
159
154
 
@@ -165,37 +160,29 @@ class DynamoDBConditionParser(ConditionParser):
165
160
  matching_operator = "begins_with"
166
161
  raw_values = [value[:-1]]
167
162
  elif value.startswith("%") and not value.endswith("%"):
168
- raise ValueError(
169
- "DynamoDB PartiQL does not directly support 'ends_with'"
170
- )
163
+ raise ValueError("DynamoDB PartiQL does not directly support 'ends_with'")
171
164
  else:
172
165
  matching_operator = "="
173
166
  raw_values = [value]
174
167
 
175
- matching_operator = self.operator_needs_remap.get(
176
- matching_operator.lower(), matching_operator
177
- )
168
+ matching_operator = self.operator_needs_remap.get(matching_operator.lower(), matching_operator)
178
169
 
179
170
  table_name: str = ""
180
171
  final_column_name: str = column
181
172
  if "." in column:
182
173
  table_prefix, column_name_part = column.split(".", 1)
183
174
  table_name = table_prefix.strip().replace('"', "").replace("`", "")
184
- final_column_name = (
185
- column_name_part.strip().replace('"', "").replace("`", "")
186
- )
175
+ final_column_name = column_name_part.strip().replace('"', "").replace("`", "")
187
176
  else:
188
177
  final_column_name = column.replace('"', "").replace("`", "")
189
178
 
190
- # This list will now correctly be List[AttributeValueTypeDef]
191
- parameters: List[AttributeValueTypeDef] = []
179
+ # This list will now correctly be list[AttributeValueTypeDef]
180
+ parameters: list[AttributeValueTypeDef] = []
192
181
  if matching_operator.lower() not in self.operators_without_placeholders:
193
182
  for val_item in raw_values:
194
183
  parameters.append(self.to_dynamodb_attribute_value(val_item))
195
184
 
196
- column_for_parsed: str = (
197
- f"{table_name}.{final_column_name}" if table_name else final_column_name
198
- )
185
+ column_for_parsed: str = f"{table_name}.{final_column_name}" if table_name else final_column_name
199
186
 
200
187
  return {
201
188
  "table": table_name,
@@ -213,19 +200,15 @@ class DynamoDBConditionParser(ConditionParser):
213
200
  self,
214
201
  column: str,
215
202
  operator: str,
216
- values: List[
217
- AttributeValueTypeDef
218
- ], # Parameter 'values' is List[AttributeValueTypeDef]
203
+ values: list[AttributeValueTypeDef], # Parameter 'values' is list[AttributeValueTypeDef]
219
204
  escape: bool = True,
220
205
  escape_character: str = '"',
221
206
  ) -> str:
222
- """
223
- Formats a SQL fragment with placeholders for a given column, operator, and parameters.
224
- """
207
+ """Format a SQL fragment with placeholders for a given column, operator, and parameters."""
225
208
  quoted_column = column
226
209
  if escape:
227
- parts: List[str] = column.split(".", 1)
228
- cleaned_parts: List[str] = [part.strip('"`') for part in parts]
210
+ parts: list[str] = column.split(".", 1)
211
+ cleaned_parts: list[str] = [part.strip('"`') for part in parts]
229
212
  if len(cleaned_parts) == 2:
230
213
  quoted_column = (
231
214
  f"{escape_character}{cleaned_parts[0]}{escape_character}"
@@ -233,9 +216,7 @@ class DynamoDBConditionParser(ConditionParser):
233
216
  f"{escape_character}{cleaned_parts[1]}{escape_character}"
234
217
  )
235
218
  else:
236
- quoted_column = (
237
- f"{escape_character}{cleaned_parts[0]}{escape_character}"
238
- )
219
+ quoted_column = f"{escape_character}{cleaned_parts[0]}{escape_character}"
239
220
 
240
221
  upper_case_operator: str = operator.upper()
241
222
  lower_case_operator: str = operator.lower()
@@ -253,12 +234,8 @@ class DynamoDBConditionParser(ConditionParser):
253
234
 
254
235
  raise ValueError(f"Unsupported operator for placeholder generation: {operator}")
255
236
 
256
- def to_dynamodb_attribute_value(
257
- self, value: Any
258
- ) -> AttributeValueTypeDef: # Return type changed
259
- """
260
- Converts a Python variable into a DynamoDB-formatted attribute value dictionary.
261
- """
237
+ def to_dynamodb_attribute_value(self, value: Any) -> AttributeValueTypeDef: # Return type changed
238
+ """Convert a Python variable into a DynamoDB-formatted attribute value dictionary."""
262
239
  if isinstance(value, str):
263
240
  if value.lower() == "true":
264
241
  return {"BOOL": True}
@@ -281,43 +258,26 @@ class DynamoDBConditionParser(ConditionParser):
281
258
  elif isinstance(value, bytes):
282
259
  return {"B": base64.b64encode(value).decode("utf-8")}
283
260
  elif isinstance(value, list):
284
- # Each item will be AttributeValueTypeDef, so the list is List[AttributeValueTypeDef]
261
+ # Each item will be AttributeValueTypeDef, so the list is list[AttributeValueTypeDef]
285
262
  return {"L": [self.to_dynamodb_attribute_value(item) for item in value]}
286
263
  elif isinstance(value, dict):
287
264
  # Each value in the map will be AttributeValueTypeDef
288
- return {
289
- "M": {
290
- str(k): self.to_dynamodb_attribute_value(v)
291
- for k, v in value.items()
292
- }
293
- }
265
+ return {"M": {str(k): self.to_dynamodb_attribute_value(v) for k, v in value.items()}}
294
266
  elif isinstance(value, set):
295
267
  if not value:
296
- raise ValueError(
297
- "Cannot determine DynamoDB Set type from an empty Python set."
298
- )
268
+ raise ValueError("Cannot determine DynamoDB Set type from an empty Python set.")
299
269
  if all(isinstance(item, str) for item in value):
300
270
  return {"SS": sorted(list(value))}
301
271
  elif all(isinstance(item, (int, float, Decimal)) for item in value):
302
272
  return {"NS": sorted([str(item) for item in value])}
303
273
  elif all(isinstance(item, bytes) for item in value):
304
- return {
305
- "BS": sorted(
306
- [base64.b64encode(item).decode("utf-8") for item in value]
307
- )
308
- }
309
- raise ValueError(
310
- "Set contains mixed types or unsupported types for DynamoDB Sets."
311
- )
274
+ return {"BS": sorted([base64.b64encode(item).decode("utf-8") for item in value])}
275
+ raise ValueError("Set contains mixed types or unsupported types for DynamoDB Sets.")
312
276
  else:
313
- raise TypeError(
314
- f"Unsupported Python type for DynamoDB conversion: {type(value)}"
315
- )
277
+ raise TypeError(f"Unsupported Python type for DynamoDB conversion: {type(value)}")
316
278
 
317
- def _parse_condition_list(self, list_string: str) -> List[str]:
318
- """
319
- Parses a string representation of a list into a list of strings.
320
- """
279
+ def _parse_condition_list(self, list_string: str) -> list[str]:
280
+ """Parse a string representation of a list into a list of strings."""
321
281
  if not list_string.strip():
322
282
  return []
323
283
 
@@ -326,7 +286,7 @@ class DynamoDBConditionParser(ConditionParser):
326
286
  if not list_string.strip():
327
287
  return []
328
288
 
329
- items: List[str] = []
289
+ items: list[str] = []
330
290
  current_item: str = ""
331
291
  in_quotes: bool = False
332
292
  quote_char: str = ""