boto3-assist 0.6.0__tar.gz → 0.6.1__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 (118) hide show
  1. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.gitignore +2 -0
  2. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.vscode/settings.json +1 -2
  3. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/PKG-INFO +1 -1
  4. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/pyproject.toml +1 -1
  5. boto3_assist-0.6.1/run_unit_tests.sh +22 -0
  6. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/models/serializable_model.py +2 -2
  7. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/serialization_utility.py +131 -11
  8. boto3_assist-0.6.1/src/boto3_assist/version.py +1 -0
  9. {boto3_assist-0.6.0/tests/utilities → boto3_assist-0.6.1/tests/__top}/__init__.py +4 -1
  10. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/cms/page.py +1 -1
  11. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/dynamodb_model_base_test.py +3 -4
  12. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/dynamodb_model_projections_test.py +2 -2
  13. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/dynamodb_model_serializtion_test.py +2 -2
  14. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/dynamodb_moto_sorting_test.py +3 -5
  15. boto3_assist-0.6.1/tests/models/serializable_model_wide_test.py +246 -0
  16. boto3_assist-0.6.1/tests/utilities/__init__.py +0 -0
  17. boto3_assist-0.6.0/src/boto3_assist/version.py +0 -1
  18. boto3_assist-0.6.0/tests/__top/__init__.py +0 -16
  19. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.env.docker +0 -0
  20. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.env.docker.001 +0 -0
  21. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.env.docker.nosql.workbench +0 -0
  22. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.env.unittest +0 -0
  23. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.vscode/launch.json +0 -0
  24. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/.vscode/tasks.json +0 -0
  25. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/LICENSE-EXPLAINED.txt +0 -0
  26. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/LICENSE.txt +0 -0
  27. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/README.md +0 -0
  28. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/aws_regions_with_status.csv +0 -0
  29. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/aws_regions_with_status.json +0 -0
  30. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/devops/build.py +0 -0
  31. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/devops/readme.md +0 -0
  32. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/__init__.py +0 -0
  33. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/cloudwatch/log_report.py +0 -0
  34. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/models/order_item_model.py +0 -0
  35. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/models/order_model.py +0 -0
  36. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/models/product_model.py +0 -0
  37. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/models/user_model.py +0 -0
  38. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/models/user_post_model.py +0 -0
  39. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/order_example/main.py +0 -0
  40. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/order_example/products.json +0 -0
  41. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/order_item_service.py +0 -0
  42. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/order_service.py +0 -0
  43. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/product_service.py +0 -0
  44. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/table_service.py +0 -0
  45. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/user_post_service.py +0 -0
  46. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/user_service.py +0 -0
  47. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/user_service_client_example.py +0 -0
  48. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/services/user_service_resource_example.py +0 -0
  49. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/dynamodb/user_post_example/main.py +0 -0
  50. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/examples/ec2/regions_report.py +0 -0
  51. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/module-headers.txt +0 -0
  52. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/mypy.ini +0 -0
  53. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/requirements-dev.txt +0 -0
  54. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/requirements.txt +0 -0
  55. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/run-checks.sh +0 -0
  56. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/__init__.py +0 -0
  57. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/aws_lambda/event_info.py +0 -0
  58. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/boto3session.py +0 -0
  59. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
  60. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
  61. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
  62. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
  63. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
  64. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
  65. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cognito/cognito_connection.py +0 -0
  66. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cognito/cognito_utility.py +0 -0
  67. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cognito/jwks_cache.py +0 -0
  68. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/cognito/user.py +0 -0
  69. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/connection.py +0 -0
  70. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/connection_tracker.py +0 -0
  71. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb.py +0 -0
  72. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
  73. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
  74. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
  75. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
  76. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
  77. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
  78. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_model_base.py +0 -0
  79. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
  80. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
  81. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
  82. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
  83. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/readme.md +0 -0
  84. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
  85. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/ec2/ec2_connection.py +0 -0
  86. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/environment_services/__init__.py +0 -0
  87. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/environment_services/environment_loader.py +0 -0
  88. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/environment_services/environment_variables.py +0 -0
  89. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/errors/custom_exceptions.py +0 -0
  90. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/http_status_codes.py +0 -0
  91. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/s3/s3.py +0 -0
  92. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/s3/s3_connection.py +0 -0
  93. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/datetime_utility.py +0 -0
  94. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/dictionaroy_utility.py +0 -0
  95. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/file_operations.py +0 -0
  96. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/http_utility.py +0 -0
  97. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/logging_utility.py +0 -0
  98. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/numbers_utility.py +0 -0
  99. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/src/boto3_assist/utilities/string_utility.py +0 -0
  100. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/__init__.py +0 -0
  101. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/__init__.py +0 -0
  102. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/cms/base.py +0 -0
  103. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/cms/content_block.py +0 -0
  104. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/cms/template.py +0 -0
  105. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/simple_model.py +0 -0
  106. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/user_model.py +0 -0
  107. {boto3_assist-0.6.0/tests/dynamodb/models → boto3_assist-0.6.1/tests/dynamodb/dbmodels}/user_required_fields_model.py +0 -0
  108. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/dynamodb/dynamodb_reindex_test.py +0 -0
  109. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/examples_test/__init__.py +0 -0
  110. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/examples_test/user_service_test.py +0 -0
  111. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/lambda/__init__.py +0 -0
  112. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/lambda/event_info_test.py +0 -0
  113. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/models/__init__.py +0 -0
  114. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/models/serializable_model_test.py +0 -0
  115. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/s3/__init__.py +0 -0
  116. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/s3/files/test.txt +0 -0
  117. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/s3/s3_file_upload_test.py +0 -0
  118. {boto3_assist-0.6.0 → boto3_assist-0.6.1}/tests/utilities/serialization_utility_test.py +0 -0
@@ -163,3 +163,5 @@ cython_debug/
163
163
 
164
164
  .imports
165
165
  .env.development
166
+ .unittest
167
+ .unittests
@@ -12,8 +12,7 @@
12
12
  "python.analysis.extraPaths": [
13
13
  "${workspaceFolder}",
14
14
  "${workspaceFolder}/examples",
15
- "${workspaceFolder}/src",
16
- "${workspaceFolder}/src/boto3_assist",
15
+ "${workspaceFolder}/src",
17
16
  "${workspaceFolder}/tests",
18
17
  "${workspaceFolder}/devops",
19
18
  "${workspaceFolder}/devops/cdk",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.6.0
3
+ Version: 0.6.1
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
@@ -7,7 +7,7 @@ packages = ["src/boto3_assist"]
7
7
 
8
8
  [project]
9
9
  name = "boto3_assist"
10
- version = "0.6.0"
10
+ version = "0.6.1"
11
11
 
12
12
  authors = [
13
13
  { name="Eric Wilson", email="boto3-assist@geekcafe.com" }
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ python --version
4
+ python -m venv .unittest
5
+ source ./.unittest/bin/activate
6
+ which python
7
+
8
+
9
+ pip install --upgrade pip
10
+ pip install -r ./requirements.txt
11
+ pip install -r ./requirements-dev.txt
12
+
13
+
14
+ echo "running unit test"
15
+ python -m unittest discover -s tests -p "*_test.py"
16
+
17
+ if [ $? -eq 0 ]; then
18
+ echo "Tests passed successfully"
19
+ else
20
+ echo "No tests found or tests failed"
21
+ exit 1
22
+ fi
@@ -5,5 +5,5 @@ MIT License. See Project Root for the license information.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
8
- from typing import TypeVar, Dict, Any
9
- from boto3_assist.utilities.serialization_utility import SerializableModel
8
+
9
+ from boto3_assist.utilities.serialization_utility import SerializableModel # noqa: F401 # pylint: disable=unused-import
@@ -1,16 +1,15 @@
1
1
  """Serialization Utility"""
2
2
 
3
- from datetime import datetime
4
- from decimal import Decimal
5
- from typing import Dict, List, TypeVar, Any
6
- import json
7
- import jsons
8
3
  import datetime as dt
9
4
  import decimal
10
5
  import inspect
6
+ import json
11
7
  import uuid
12
- from aws_lambda_powertools import Logger
8
+ from datetime import datetime
9
+ from decimal import Decimal
10
+ from typing import Any, Dict, List, TypeVar
13
11
 
12
+ from aws_lambda_powertools import Logger
14
13
 
15
14
  T = TypeVar("T")
16
15
 
@@ -53,6 +52,15 @@ class SerializableModel:
53
52
  instance=self, serialize_fn=lambda x: x, include_none=True
54
53
  )
55
54
 
55
+ def to_wide_dictionary(self) -> Dict:
56
+ """
57
+ Dumps an object to dictionary structure
58
+ """
59
+
60
+ dump = Serialization.to_wide_dictionary(model=self)
61
+
62
+ return dump
63
+
56
64
 
57
65
  class JsonEncoder(json.JSONEncoder):
58
66
  """
@@ -108,6 +116,32 @@ class Serialization:
108
116
 
109
117
  return dump
110
118
 
119
+ @staticmethod
120
+ def to_wide_dictionary(model: object) -> Dict:
121
+ """
122
+ Dumps an object to dictionary structure
123
+ """
124
+
125
+ dump = Serialization.to_dict(
126
+ instance=model, serialize_fn=lambda x: x, include_none=True
127
+ )
128
+
129
+ # have a dictionary now let's flatten out
130
+ flat_dict = {}
131
+ for key, value in dump.items():
132
+ if isinstance(value, dict):
133
+ for sub_key, sub_value in value.items():
134
+ flat_dict[f"{key}_{sub_key}"] = sub_value
135
+ elif isinstance(value, list):
136
+ for i, sub_value in enumerate(value):
137
+ sub_dict = Serialization.to_wide_dictionary(sub_value)
138
+ for sub_key, sub_value in sub_dict.items():
139
+ flat_dict[f"{key}_{i}_{sub_key}"] = sub_value
140
+ else:
141
+ flat_dict[key] = value
142
+
143
+ return flat_dict
144
+
111
145
  @staticmethod
112
146
  def map(source: object, target: T, coerce: bool = True) -> T | None:
113
147
  """Map an object from one object to another"""
@@ -118,12 +152,92 @@ class Serialization:
118
152
  source_dict = Serialization.convert_object_to_dict(source)
119
153
  if not isinstance(source_dict, dict):
120
154
  return None
121
- return Serialization.load_properties(
155
+ return Serialization._load_properties(
122
156
  source=source_dict, target=target, coerce=coerce
123
157
  )
124
158
 
125
159
  @staticmethod
126
- def load_properties(
160
+ def to_wide_dictionary_list(
161
+ data: Dict[str, Any] | List[Dict[str, Any]],
162
+ remove_collisions: bool = True,
163
+ raise_error_on_collision: bool = False,
164
+ ) -> List[Dict[str, Any]]:
165
+ """
166
+ Converts a dictionary or list of dictionaries to a list of dictionaries.
167
+
168
+ :param data: Dictionary or list of dictionaries to be converted
169
+ :param remove_collisions: If True, removes duplicate keys from the dictionaries
170
+ :return: List of dictionaries
171
+ """
172
+
173
+ collisions = []
174
+
175
+ def recursive_flatten(prefix, obj):
176
+ """
177
+ Recursively flattens a JSON object.
178
+
179
+ :param prefix: Current key prefix
180
+ :param obj: Object to flatten
181
+ :return: List of flattened dictionaries
182
+ """
183
+ if isinstance(obj, list):
184
+ result = []
185
+ for _, item in enumerate(obj):
186
+ x = recursive_flatten("", item)
187
+ result.extend(x)
188
+ return result
189
+ elif isinstance(obj, dict):
190
+ result = [{}]
191
+ for key, value in obj.items():
192
+ sub_result = recursive_flatten(
193
+ f"{prefix}_{key}" if prefix else key, value
194
+ )
195
+ new_result = []
196
+ for entry in result:
197
+ for sub_entry in sub_result:
198
+ # remove any collisions
199
+
200
+ for k in entry:
201
+ if k in sub_entry:
202
+ if k not in collisions:
203
+ logger.debug(f"Collision detected: {k}")
204
+ collisions.append(k)
205
+ merged = entry.copy()
206
+ merged.update(sub_entry)
207
+ new_result.append(merged)
208
+ result = new_result
209
+ return result
210
+ else:
211
+ return [{prefix: obj}] if prefix else []
212
+
213
+ results = recursive_flatten("", data)
214
+ if remove_collisions:
215
+ results = Serialization.remove_collisions(results, collisions)
216
+
217
+ if raise_error_on_collision and len(collisions) > 0:
218
+ raise ValueError(f"Duplicate keys detected: {collisions}")
219
+
220
+ return results
221
+
222
+ @staticmethod
223
+ def remove_collisions(
224
+ data: List[Dict[str, Any]], collisions: List[str]
225
+ ) -> List[Dict[str, Any]]:
226
+ """
227
+ Removes collisions from a list of dictionaries.
228
+
229
+ :param data: List of dictionaries
230
+ :param collisions: List of collision keys
231
+ :return: List of dictionaries with collisions removed
232
+ """
233
+ for c in collisions:
234
+ for r in data:
235
+ if c in r:
236
+ del r[c]
237
+ return data
238
+
239
+ @staticmethod
240
+ def _load_properties(
127
241
  source: dict,
128
242
  target: T,
129
243
  coerce: bool = True,
@@ -183,9 +297,9 @@ class Serialization:
183
297
  attr.clear()
184
298
  attr.extend(value)
185
299
  elif isinstance(attr, dict) and isinstance(value, dict):
186
- Serialization.load_properties(value, attr, coerce=coerce)
300
+ Serialization._load_properties(value, attr, coerce=coerce)
187
301
  elif hasattr(attr, "__dict__") and isinstance(value, dict):
188
- Serialization.load_properties(value, attr, coerce=coerce)
302
+ Serialization._load_properties(value, attr, coerce=coerce)
189
303
  else:
190
304
  setattr(target, key, value)
191
305
  except ValueError as e:
@@ -230,12 +344,18 @@ class Serialization:
230
344
 
231
345
  @staticmethod
232
346
  def to_dict(
233
- instance: SerializableModel,
347
+ instance: SerializableModel | dict,
234
348
  serialize_fn,
235
349
  include_none: bool = True,
236
350
  ) -> Dict[str, Any]:
237
351
  """To Dict / Dictionary"""
238
352
 
353
+ if instance is None:
354
+ return {}
355
+
356
+ if isinstance(instance, dict):
357
+ return instance
358
+
239
359
  def is_primitive(value):
240
360
  """Check if the value is a primitive data type."""
241
361
  return isinstance(value, (str, int, bool, type(None)))
@@ -0,0 +1 @@
1
+ __version__ = '0.6.1'
@@ -9,8 +9,11 @@ import sys
9
9
  from pathlib import Path
10
10
 
11
11
  ## needed for discovery based top level execution
12
+ print("👋 init test paths for __top")
12
13
  root_directory = Path(__file__).resolve().parent.parent.parent
13
14
  src_directory = os.path.join(root_directory, "src")
14
15
 
15
16
  sys.path.insert(0, src_directory)
16
- print(sys.path)
17
+ print("")
18
+ for p in sys.path:
19
+ print(f"👉 {p}")
@@ -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.models.cms.base import BaseCMSDBModel
10
+ from tests.dynamodb.dbmodels.cms.base import BaseCMSDBModel
11
11
 
12
12
 
13
13
  class Page(BaseCMSDBModel):
@@ -5,12 +5,11 @@ MIT License. See Project Root for the license information.
5
5
  """
6
6
 
7
7
  import unittest
8
- from typing import List, Dict
9
-
8
+ from typing import Dict, List
10
9
 
11
10
  from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex
12
- from src.boto3_assist.dynamodb.dynamodb_key import DynamoDBKey
13
- from tests.dynamodb.models.user_model import User
11
+ from boto3_assist.dynamodb.dynamodb_key import DynamoDBKey
12
+ from tests.dynamodb.dbmodels.user_model import User
14
13
 
15
14
 
16
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.models.user_model import User
11
- from tests.dynamodb.models.simple_model import Simple
10
+ from tests.dynamodb.dbmodels.user_model import User
11
+ from tests.dynamodb.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.models.user_model import User
11
- from tests.dynamodb.models.user_required_fields_model import User as User2
10
+ from tests.dynamodb.dbmodels.user_model import User
11
+ from tests.dynamodb.dbmodels.user_required_fields_model import User as User2
12
12
 
13
13
 
14
14
  class DynamoDBModelSerializationUnitTest(unittest.TestCase):
@@ -4,17 +4,15 @@ Maintainers: Eric Wilson
4
4
  MIT License. See Project Root for the license information.
5
5
  """
6
6
 
7
- import os
8
7
  import unittest
9
- import moto
10
8
  from typing import List
11
9
 
12
-
10
+ import moto
13
11
  from mypy_boto3_dynamodb import DynamoDBClient
14
12
 
15
- from dynamodb.models.cms.page import Page
16
- from boto3_assist.environment_services.environment_loader import EnvironmentLoader
17
13
  from boto3_assist.dynamodb.dynamodb import DynamoDB
14
+ from boto3_assist.environment_services.environment_loader import EnvironmentLoader
15
+ from tests.dynamodb.dbmodels.cms.page import Page
18
16
 
19
17
 
20
18
  @moto.mock_aws
@@ -0,0 +1,246 @@
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
+
9
+ from boto3_assist.models.serializable_model import SerializableModel
10
+ from boto3_assist.utilities.serialization_utility import Serialization
11
+
12
+
13
+ class Address(SerializableModel):
14
+ """A model that inherits the serializable"""
15
+
16
+ def __init__(
17
+ self,
18
+ *,
19
+ id: str | None = None, # pylint: disable=redefined-builtin
20
+ street: str | None = None,
21
+ city: str | None = None,
22
+ state: str | None = None,
23
+ zip: str | None = None, # pylint: disable=redefined-builtin
24
+ ):
25
+ self.id: str | None = id
26
+ self.street: str | None = street
27
+ self.city: str | None = city
28
+ self.state: str | None = state
29
+ self.zip: str | None = zip
30
+
31
+
32
+ class User(SerializableModel):
33
+ """A model that inherits the serializable"""
34
+
35
+ def __init__(self):
36
+ self.__id: str | None = None
37
+ self.first_name: str = ""
38
+ self.last_name: str = ""
39
+ self.addresses: list[Address] = []
40
+
41
+ @property
42
+ def id(self) -> str:
43
+ """The id of the order"""
44
+ return self.__id
45
+
46
+ @id.setter
47
+ def id(self, value: str) -> None:
48
+ self.__id = value
49
+
50
+
51
+ class TestSerializableModel(unittest.TestCase):
52
+ """Testing Serialzing Models"""
53
+
54
+ def setUp(self) -> None:
55
+ pass
56
+
57
+ def test_map_valid_data(self):
58
+ """
59
+ Test mapping a valid dictionary to an object instance.
60
+ """
61
+ user: User = User()
62
+ user.id = "1"
63
+ user.first_name = "John"
64
+ user.last_name = "Smith"
65
+ for i in range(3):
66
+ user.addresses.append(
67
+ Address(
68
+ id=str(i),
69
+ street=f"10{i} S. Main St.",
70
+ city="Anytown",
71
+ state="AnyWhere",
72
+ zip="00001",
73
+ )
74
+ )
75
+
76
+ flat = user.to_wide_dictionary()
77
+
78
+ self.assertIsNotNone(flat)
79
+ self.assertEqual(flat["id"], "1")
80
+ self.assertEqual(flat["first_name"], "John")
81
+ self.assertEqual(flat["last_name"], "Smith")
82
+ self.assertEqual(flat["addresses_0_id"], "0")
83
+ self.assertEqual(flat["addresses_0_street"], "100 S. Main St.")
84
+ self.assertEqual(flat["addresses_0_city"], "Anytown")
85
+ self.assertEqual(flat["addresses_0_state"], "AnyWhere")
86
+ self.assertEqual(flat["addresses_0_zip"], "00001")
87
+
88
+ def test_dict_wide(self):
89
+ model = {
90
+ "id": "1",
91
+ "first_name": "John",
92
+ "last_name": "Smith",
93
+ "addresses": [
94
+ {
95
+ "id": "0",
96
+ "street": "100 S. Main St.",
97
+ "city": "Anytown",
98
+ "state": "AnyWhere",
99
+ "zip": "00001",
100
+ },
101
+ {
102
+ "id": "1",
103
+ "street": "101 S. Main St.",
104
+ "city": "Anytown",
105
+ "state": "AnyWhere",
106
+ "zip": "00001",
107
+ },
108
+ ],
109
+ }
110
+
111
+ flat = Serialization.to_wide_dictionary(model)
112
+
113
+ self.assertIsNotNone(flat)
114
+ self.assertEqual(flat["id"], "1")
115
+ self.assertEqual(flat["first_name"], "John")
116
+ self.assertEqual(flat["last_name"], "Smith")
117
+ self.assertEqual(flat["addresses_0_id"], "0")
118
+ self.assertEqual(flat["addresses_0_street"], "100 S. Main St.")
119
+ self.assertEqual(flat["addresses_0_city"], "Anytown")
120
+ self.assertEqual(flat["addresses_0_state"], "AnyWhere")
121
+ self.assertEqual(flat["addresses_0_zip"], "00001")
122
+
123
+ def test_dict_multi_level_array(self):
124
+ model = {
125
+ "id": "1",
126
+ "level_one_array": [
127
+ {
128
+ "id": "1",
129
+ "level_two_array": [
130
+ {
131
+ "id": "1",
132
+ "level_three_array": [
133
+ {
134
+ "id": "1",
135
+ }
136
+ ],
137
+ }
138
+ ],
139
+ }
140
+ ],
141
+ }
142
+
143
+ flat = Serialization.to_wide_dictionary(model)
144
+
145
+ self.assertIsNotNone(flat)
146
+ self.assertEqual(flat["id"], "1")
147
+ self.assertEqual(flat["level_one_array_0_id"], "1")
148
+ self.assertEqual(flat["level_one_array_0_level_two_array_0_id"], "1")
149
+ self.assertEqual(
150
+ flat["level_one_array_0_level_two_array_0_level_three_array_0_id"], "1"
151
+ )
152
+
153
+ def test_dict_wide_list_happy_path(self):
154
+ model = {
155
+ "first_name": "John",
156
+ "last_name": "Smith",
157
+ "addresses": [
158
+ {
159
+ "street": "100 S. Main St.",
160
+ "city": "Anytown",
161
+ "state": "AnyWhere",
162
+ "zip": "00001",
163
+ },
164
+ {
165
+ "street": "101 S. Main St.",
166
+ "city": "Anytown",
167
+ "state": "AnyWhere",
168
+ "zip": "00001",
169
+ },
170
+ ],
171
+ }
172
+
173
+ expected_results = [
174
+ {
175
+ "first_name": "John",
176
+ "last_name": "Smith",
177
+ "street": "100 S. Main St.",
178
+ "city": "Anytown",
179
+ "state": "AnyWhere",
180
+ "zip": "00001",
181
+ },
182
+ {
183
+ "first_name": "John",
184
+ "last_name": "Smith",
185
+ "street": "101 S. Main St.",
186
+ "city": "Anytown",
187
+ "state": "AnyWhere",
188
+ "zip": "00001",
189
+ },
190
+ ]
191
+
192
+ flat = Serialization.to_wide_dictionary_list(model)
193
+
194
+ self.assertIsNotNone(flat)
195
+ self.assertEqual(flat, expected_results)
196
+
197
+ def test_dict_wide_list_duplicate_keys(self):
198
+ model = {
199
+ "id": "1",
200
+ "first_name": "John",
201
+ "last_name": "Smith",
202
+ "addresses": [
203
+ {
204
+ "id": "a",
205
+ "street": "100 S. Main St.",
206
+ "city": "Anytown",
207
+ "state": "AnyWhere",
208
+ "zip": "00001",
209
+ },
210
+ {
211
+ "id": "b",
212
+ "street": "101 S. Main St.",
213
+ "city": "Anytown",
214
+ "state": "AnyWhere",
215
+ "zip": "00001",
216
+ },
217
+ ],
218
+ }
219
+
220
+ expected_results = [
221
+ {
222
+ "first_name": "John",
223
+ "last_name": "Smith",
224
+ "street": "100 S. Main St.",
225
+ "city": "Anytown",
226
+ "state": "AnyWhere",
227
+ "zip": "00001",
228
+ },
229
+ {
230
+ "first_name": "John",
231
+ "last_name": "Smith",
232
+ "street": "101 S. Main St.",
233
+ "city": "Anytown",
234
+ "state": "AnyWhere",
235
+ "zip": "00001",
236
+ },
237
+ ]
238
+
239
+ flat = Serialization.to_wide_dictionary_list(model)
240
+
241
+ self.assertIsNotNone(flat)
242
+ self.assertEqual(flat, expected_results)
243
+
244
+
245
+ if __name__ == "__main__":
246
+ unittest.main()
File without changes
@@ -1 +0,0 @@
1
- __version__ = '0.6.0'
@@ -1,16 +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
- ## needed for discovery based top level execution
12
- root_directory = Path(__file__).resolve().parent.parent.parent
13
- src_directory = os.path.join(root_directory, "src")
14
-
15
- sys.path.insert(0, src_directory)
16
- print(sys.path)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes