boto3-assist 0.1.1__tar.gz → 0.1.3__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.
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/PKG-INFO +1 -1
- boto3_assist-0.1.3/aws_regions_with_status.csv +31 -0
- boto3_assist-0.1.3/aws_regions_with_status.json +122 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/devops/readme.md +1 -3
- boto3_assist-0.1.1/examples/dynamodb/models/order_item.py → boto3_assist-0.1.3/examples/dynamodb/models/order_item_model.py +6 -5
- boto3_assist-0.1.3/examples/dynamodb/models/order_model.py +101 -0
- boto3_assist-0.1.1/examples/dynamodb/models/order_model.py → boto3_assist-0.1.3/examples/dynamodb/models/product_model.py +23 -20
- boto3_assist-0.1.3/examples/dynamodb/order_example/main.py +168 -0
- boto3_assist-0.1.3/examples/dynamodb/order_example/products.json +352 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/order_item_service.py +1 -1
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/order_service.py +36 -12
- boto3_assist-0.1.3/examples/ec2/regions_report.py +80 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/pyproject.toml +1 -1
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/requirements-dev.txt +1 -0
- boto3_assist-0.1.3/src/boto3_assist/connection_tracker.py +47 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_index.py +18 -6
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_key.py +3 -2
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_model_base.py +8 -0
- boto3_assist-0.1.3/src/boto3_assist/ec2/ec2_connection.py +103 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/environment_services/environment_variables.py +16 -0
- boto3_assist-0.1.3/src/boto3_assist/version.py +1 -0
- boto3_assist-0.1.3/tests/examples_test/__init__.py +0 -0
- boto3_assist-0.1.1/examples/dynamodb/order_example/main.py +0 -93
- boto3_assist-0.1.1/src/boto3_assist/version.py +0 -1
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.env.development +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.env.docker +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.env.docker.001 +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.env.docker.nosql.workbench +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.gitignore +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.vscode/launch.json +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.vscode/settings.json +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/.vscode/tasks.json +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/LICENSE-EXPLAINED.txt +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/LICENSE.txt +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/README.md +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/devops/build.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/models/user_model.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/models/user_post_model.py +0 -0
- /boto3_assist-0.1.1/src/boto3_assist/__init__.py → /boto3_assist-0.1.3/examples/dynamodb/services/product_service.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/table_service.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/user_post_service.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/user_service.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/user_service_client_example.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/services/user_service_resource_example.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/examples/dynamodb/user_post_example/main.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/module-headers.txt +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/mypy.ini +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/requirements.txt +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/run-checks.sh +0 -0
- {boto3_assist-0.1.1/src/boto3_assist/environment_services → boto3_assist-0.1.3/src/boto3_assist}/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/boto3session.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_connection_tracker.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/readme.md +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
- {boto3_assist-0.1.1/tests → boto3_assist-0.1.3/src/boto3_assist/environment_services}/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/environment_services/environment_loader.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/utilities/datetime_utility.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/utilities/logging_utility.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/utilities/serialization_utility.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/src/boto3_assist/utilities/string_utility.py +0 -0
- {boto3_assist-0.1.1/tests/dynamodb → boto3_assist-0.1.3/tests}/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/__top/__init__.py +0 -0
- {boto3_assist-0.1.1/tests/examples_test → boto3_assist-0.1.3/tests/dynamodb}/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/dynamodb/dynamodb_model_base_test.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/dynamodb/dynamodb_reindex_test.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/examples_test/user_service_test.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/utilities/__init__.py +0 -0
- {boto3_assist-0.1.1 → boto3_assist-0.1.3}/tests/utilities/serialization_utility_test.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
RegionName,OptInStatus
|
|
2
|
+
af-south-1,not-opted-in
|
|
3
|
+
ap-east-1,not-opted-in
|
|
4
|
+
ap-northeast-1,opt-in-not-required
|
|
5
|
+
ap-northeast-2,opt-in-not-required
|
|
6
|
+
ap-northeast-3,opt-in-not-required
|
|
7
|
+
ap-south-1,opt-in-not-required
|
|
8
|
+
ap-south-2,not-opted-in
|
|
9
|
+
ap-southeast-1,opt-in-not-required
|
|
10
|
+
ap-southeast-2,opt-in-not-required
|
|
11
|
+
ap-southeast-3,not-opted-in
|
|
12
|
+
ap-southeast-4,not-opted-in
|
|
13
|
+
ap-southeast-5,not-opted-in
|
|
14
|
+
ca-central-1,opt-in-not-required
|
|
15
|
+
ca-west-1,not-opted-in
|
|
16
|
+
eu-central-1,opt-in-not-required
|
|
17
|
+
eu-central-2,not-opted-in
|
|
18
|
+
eu-north-1,opt-in-not-required
|
|
19
|
+
eu-south-1,not-opted-in
|
|
20
|
+
eu-south-2,not-opted-in
|
|
21
|
+
eu-west-1,opt-in-not-required
|
|
22
|
+
eu-west-2,opt-in-not-required
|
|
23
|
+
eu-west-3,opt-in-not-required
|
|
24
|
+
il-central-1,not-opted-in
|
|
25
|
+
me-central-1,not-opted-in
|
|
26
|
+
me-south-1,not-opted-in
|
|
27
|
+
sa-east-1,opt-in-not-required
|
|
28
|
+
us-east-1,opt-in-not-required
|
|
29
|
+
us-east-2,opt-in-not-required
|
|
30
|
+
us-west-1,opt-in-not-required
|
|
31
|
+
us-west-2,opt-in-not-required
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"RegionName": "af-south-1",
|
|
4
|
+
"OptInStatus": "not-opted-in"
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"RegionName": "ap-east-1",
|
|
8
|
+
"OptInStatus": "not-opted-in"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"RegionName": "ap-northeast-1",
|
|
12
|
+
"OptInStatus": "opt-in-not-required"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"RegionName": "ap-northeast-2",
|
|
16
|
+
"OptInStatus": "opt-in-not-required"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"RegionName": "ap-northeast-3",
|
|
20
|
+
"OptInStatus": "opt-in-not-required"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"RegionName": "ap-south-1",
|
|
24
|
+
"OptInStatus": "opt-in-not-required"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"RegionName": "ap-south-2",
|
|
28
|
+
"OptInStatus": "not-opted-in"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"RegionName": "ap-southeast-1",
|
|
32
|
+
"OptInStatus": "opt-in-not-required"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"RegionName": "ap-southeast-2",
|
|
36
|
+
"OptInStatus": "opt-in-not-required"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"RegionName": "ap-southeast-3",
|
|
40
|
+
"OptInStatus": "not-opted-in"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"RegionName": "ap-southeast-4",
|
|
44
|
+
"OptInStatus": "not-opted-in"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"RegionName": "ap-southeast-5",
|
|
48
|
+
"OptInStatus": "not-opted-in"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"RegionName": "ca-central-1",
|
|
52
|
+
"OptInStatus": "opt-in-not-required"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"RegionName": "ca-west-1",
|
|
56
|
+
"OptInStatus": "not-opted-in"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"RegionName": "eu-central-1",
|
|
60
|
+
"OptInStatus": "opt-in-not-required"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"RegionName": "eu-central-2",
|
|
64
|
+
"OptInStatus": "not-opted-in"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"RegionName": "eu-north-1",
|
|
68
|
+
"OptInStatus": "opt-in-not-required"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"RegionName": "eu-south-1",
|
|
72
|
+
"OptInStatus": "not-opted-in"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"RegionName": "eu-south-2",
|
|
76
|
+
"OptInStatus": "not-opted-in"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"RegionName": "eu-west-1",
|
|
80
|
+
"OptInStatus": "opt-in-not-required"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"RegionName": "eu-west-2",
|
|
84
|
+
"OptInStatus": "opt-in-not-required"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"RegionName": "eu-west-3",
|
|
88
|
+
"OptInStatus": "opt-in-not-required"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"RegionName": "il-central-1",
|
|
92
|
+
"OptInStatus": "not-opted-in"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"RegionName": "me-central-1",
|
|
96
|
+
"OptInStatus": "not-opted-in"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"RegionName": "me-south-1",
|
|
100
|
+
"OptInStatus": "not-opted-in"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"RegionName": "sa-east-1",
|
|
104
|
+
"OptInStatus": "opt-in-not-required"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"RegionName": "us-east-1",
|
|
108
|
+
"OptInStatus": "opt-in-not-required"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"RegionName": "us-east-2",
|
|
112
|
+
"OptInStatus": "opt-in-not-required"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"RegionName": "us-west-1",
|
|
116
|
+
"OptInStatus": "opt-in-not-required"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"RegionName": "us-west-2",
|
|
120
|
+
"OptInStatus": "opt-in-not-required"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
@@ -20,6 +20,4 @@ twine upload --repository-url https://test.pypi.org/legacy/ dist/*
|
|
|
20
20
|
|
|
21
21
|
pip install --index-url https://test.pypi.org/simple/ boto3-assist
|
|
22
22
|
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
pypi-AgEIcHlwaS5vcmcCJGQ0Zjk3ODRmLTgxZDMtNDViNy1iN2QwLWZkZDQzNTEyY2UwMgACKlszLCI3YzZmZTIzYS04MDVkLTQ0YjYtODlkZS04YTJiODJiNjBmMDQiXQAABiBxfnzdKtx6gff_FbuwKKV8D4b7XkUJJESxSqU1_mkCRA
|
|
23
|
+
```
|
|
@@ -8,6 +8,7 @@ import datetime
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
from boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
|
|
10
10
|
from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
|
|
11
|
+
from examples.dynamodb.models.product_model import Product
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class OrderItem(DynamoDBModelBase):
|
|
@@ -17,12 +18,12 @@ class OrderItem(DynamoDBModelBase):
|
|
|
17
18
|
super().__init__()
|
|
18
19
|
self.id: Optional[str] = None
|
|
19
20
|
self.order_id: Optional[str] = None
|
|
20
|
-
|
|
21
|
-
self.product_id: Optional[str] = None
|
|
22
|
-
self.product_name: Optional[str] = None
|
|
21
|
+
self.product: Optional[Product] = None
|
|
22
|
+
# self.product_id: Optional[str] = None
|
|
23
|
+
# self.product_name: Optional[str] = None
|
|
23
24
|
self.quantity: Optional[int] = 0
|
|
24
|
-
self.price: Optional[float] = 0.0
|
|
25
|
-
self.is_taxable: bool = False
|
|
25
|
+
# self.price: Optional[float] = 0.0
|
|
26
|
+
# self.is_taxable: bool = False
|
|
26
27
|
self.is_discounted: bool = False
|
|
27
28
|
self.created_utc: Optional[datetime.datetime] = None
|
|
28
29
|
self.modified_utc: Optional[datetime.datetime] = None
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geek Cafe, LLC
|
|
3
|
+
Maintainers: Eric Wilson
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import datetime
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
|
|
10
|
+
from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Order(DynamoDBModelBase):
|
|
14
|
+
"""Order Model"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
id: Optional[str] = None, # pylint: disable=w0622
|
|
19
|
+
) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.id: Optional[str] = id
|
|
22
|
+
self.user_id: Optional[str] = None
|
|
23
|
+
self.created_utc: Optional[datetime.datetime] = None
|
|
24
|
+
self.modified_utc: Optional[datetime.datetime] = None
|
|
25
|
+
self.completed_utc: Optional[datetime.datetime] = None
|
|
26
|
+
self.status: Optional[str] = None
|
|
27
|
+
self.total: float = 0
|
|
28
|
+
self.tax_total: float = 0
|
|
29
|
+
self.__setup_indexes()
|
|
30
|
+
|
|
31
|
+
def get_completed_utc_ts(self) -> float | str:
|
|
32
|
+
"""Get a time stamp representation of the completed date"""
|
|
33
|
+
if self.completed_utc is None:
|
|
34
|
+
return ""
|
|
35
|
+
return self.completed_utc.timestamp()
|
|
36
|
+
|
|
37
|
+
def get_completed_utc_yyyymmdd(self) -> str:
|
|
38
|
+
"""Get a time stamp representation of the completed date"""
|
|
39
|
+
if self.completed_utc is None:
|
|
40
|
+
return "yyyymmdd"
|
|
41
|
+
value = f"{self.completed_utc.year}{str(self.completed_utc.month).zfill(2)}{str(self.completed_utc.day).zfill(2)}"
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
def __setup_indexes(self):
|
|
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)
|
|
53
|
+
|
|
54
|
+
# all orders on a given day, sort by created date
|
|
55
|
+
self.indexes.add_secondary(
|
|
56
|
+
DynamoDBIndex(
|
|
57
|
+
index_name="gsi0",
|
|
58
|
+
partition_key=DynamoDBKey(
|
|
59
|
+
attribute_name="gsi0_pk",
|
|
60
|
+
value=lambda: DynamoDBKey.build_key(
|
|
61
|
+
(
|
|
62
|
+
"yyyymmdd",
|
|
63
|
+
f"{self.get_completed_utc_yyyymmdd()}",
|
|
64
|
+
)
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
sort_key=DynamoDBKey(
|
|
68
|
+
attribute_name="gsi0_sk",
|
|
69
|
+
value=lambda: DynamoDBKey.build_key(
|
|
70
|
+
# when dealing with things like timestamps (or any numberfields)
|
|
71
|
+
# you may want to exclude a prefix key
|
|
72
|
+
# e.g. instead of: ("completed_utc_ts", self.get_completed_utc_ts())
|
|
73
|
+
# do the following by passing an empty string
|
|
74
|
+
("", self.get_completed_utc_ts())
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# all user orders, sort by completed date
|
|
81
|
+
self.indexes.add_secondary(
|
|
82
|
+
DynamoDBIndex(
|
|
83
|
+
index_name="gsi1",
|
|
84
|
+
partition_key=DynamoDBKey(
|
|
85
|
+
attribute_name="gsi1_pk",
|
|
86
|
+
value=lambda: DynamoDBKey.build_key(
|
|
87
|
+
("user", self.user_id), ("orders", "")
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
sort_key=DynamoDBKey(
|
|
91
|
+
attribute_name="gsi1_sk",
|
|
92
|
+
value=lambda: DynamoDBKey.build_key(
|
|
93
|
+
# when dealing with things like timestamps (or any numberfields)
|
|
94
|
+
# you may want to exclude a prefix key
|
|
95
|
+
# e.g. instead of: ("completed_utc_ts", self.get_completed_utc_ts())
|
|
96
|
+
# do the following by passing an empty string
|
|
97
|
+
(None, self.get_completed_utc_ts())
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
)
|
|
@@ -10,29 +10,35 @@ from boto3_assist.dynamodb.dynamodb_model_base import DynamoDBModelBase
|
|
|
10
10
|
from boto3_assist.dynamodb.dynamodb_index import DynamoDBIndex, DynamoDBKey
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
class Product(DynamoDBModelBase):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
id: Optional[str] = None, # pylint: disable=w0622
|
|
17
|
+
name: Optional[str] = None,
|
|
18
|
+
price: float = 0.0,
|
|
19
|
+
description: Optional[str] = None,
|
|
20
|
+
sku: Optional[str] = None,
|
|
21
|
+
):
|
|
17
22
|
super().__init__()
|
|
23
|
+
|
|
18
24
|
self.id: Optional[str] = id
|
|
19
|
-
self.
|
|
20
|
-
self.
|
|
21
|
-
self.
|
|
22
|
-
self.
|
|
23
|
-
|
|
24
|
-
self.total: float = 0
|
|
25
|
-
self.tax_total: float = 0
|
|
25
|
+
self.name: Optional[str] = name
|
|
26
|
+
self.price: float = price
|
|
27
|
+
self.description: Optional[str] = description
|
|
28
|
+
self.sku: Optional[str] = sku
|
|
29
|
+
|
|
26
30
|
self.__setup_indexes()
|
|
27
31
|
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return f"{self.name} - ${self.price}"
|
|
34
|
+
|
|
28
35
|
def __setup_indexes(self):
|
|
29
|
-
# user id
|
|
30
36
|
primay: DynamoDBIndex = DynamoDBIndex()
|
|
31
37
|
primay.name = "primary"
|
|
32
38
|
primay.partition_key.attribute_name = "pk"
|
|
33
|
-
primay.partition_key.value = lambda: DynamoDBKey.build_key(("
|
|
39
|
+
primay.partition_key.value = lambda: DynamoDBKey.build_key(("product", self.id))
|
|
34
40
|
primay.sort_key.attribute_name = "sk"
|
|
35
|
-
primay.sort_key.value = lambda: DynamoDBKey.build_key(("
|
|
41
|
+
primay.sort_key.value = lambda: DynamoDBKey.build_key(("product", self.id))
|
|
36
42
|
self.indexes.add_primary(primay)
|
|
37
43
|
|
|
38
44
|
self.indexes.add_secondary(
|
|
@@ -40,15 +46,12 @@ class Order(DynamoDBModelBase):
|
|
|
40
46
|
index_name="gsi0",
|
|
41
47
|
partition_key=DynamoDBKey(
|
|
42
48
|
attribute_name="gsi0_pk",
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
),
|
|
49
|
+
# hot key warning
|
|
50
|
+
value=lambda: DynamoDBKey.build_key(("products", "")),
|
|
46
51
|
),
|
|
47
52
|
sort_key=DynamoDBKey(
|
|
48
53
|
attribute_name="gsi0_sk",
|
|
49
|
-
value=lambda: DynamoDBKey.build_key(
|
|
50
|
-
("completed_utc", self.created_utc)
|
|
51
|
-
),
|
|
54
|
+
value=lambda: DynamoDBKey.build_key(("name", self.name)),
|
|
52
55
|
),
|
|
53
56
|
)
|
|
54
57
|
)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DynamoDB Example
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import random
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List
|
|
10
|
+
from datetime import datetime, timedelta, UTC
|
|
11
|
+
from boto3_assist.dynamodb.dynamodb import DynamoDB
|
|
12
|
+
from boto3_assist.environment_services.environment_loader import EnvironmentLoader
|
|
13
|
+
from boto3_assist.utilities.serialization_utility import JsonEncoder
|
|
14
|
+
from boto3_assist.utilities.string_utility import StringUtility
|
|
15
|
+
from examples.dynamodb.services.table_service import DynamoDBTableService
|
|
16
|
+
from examples.dynamodb.services.order_service import OrderService, Order
|
|
17
|
+
from examples.dynamodb.services.order_item_service import OrderItemService, OrderItem
|
|
18
|
+
from examples.dynamodb.models.product_model import Product
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DynamoDBExample:
|
|
22
|
+
"""An example of using and debuggin DynamoDB"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, table_name: str) -> None:
|
|
25
|
+
self.db: DynamoDB = DynamoDB()
|
|
26
|
+
self.table_service: DynamoDBTableService = DynamoDBTableService(self.db)
|
|
27
|
+
|
|
28
|
+
self.table_name = table_name
|
|
29
|
+
|
|
30
|
+
self.order_service: OrderService = OrderService(self.db, table_name=table_name)
|
|
31
|
+
self.order_item_service: OrderItemService = OrderItemService(
|
|
32
|
+
self.db, table_name=table_name
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
self.__products: List[Product] = []
|
|
36
|
+
self.order_ids: list[str] = []
|
|
37
|
+
|
|
38
|
+
self.__load_products()
|
|
39
|
+
|
|
40
|
+
def __load_products(self):
|
|
41
|
+
"""Load products"""
|
|
42
|
+
path = os.path.join(str(Path(__file__).parent.absolute()), "products.json")
|
|
43
|
+
if os.path.exists(path):
|
|
44
|
+
with open(path, "r", encoding="utf-8") as file:
|
|
45
|
+
prodcut_list = json.load(file)
|
|
46
|
+
self.__products = [Product(**product) for product in prodcut_list]
|
|
47
|
+
else:
|
|
48
|
+
raise FileNotFoundError("Failed to find the products file")
|
|
49
|
+
|
|
50
|
+
def run_examples(self):
|
|
51
|
+
"""Run a basic examples with some CRUD examples"""
|
|
52
|
+
|
|
53
|
+
# I'm going to use a single table design pattern but you don't have to
|
|
54
|
+
self.table_service.create_a_table(table_name=self.table_name)
|
|
55
|
+
user_1 = StringUtility.generate_uuid()
|
|
56
|
+
five_days_ago = datetime.now(UTC) - timedelta(days=5)
|
|
57
|
+
four_days_ago = datetime.now(UTC) - timedelta(days=4)
|
|
58
|
+
user_2 = StringUtility.generate_uuid()
|
|
59
|
+
user_3 = StringUtility.generate_uuid()
|
|
60
|
+
user_4 = StringUtility.generate_uuid()
|
|
61
|
+
self.__generate_order(user_id=user_1, override_competed_date_utc=five_days_ago)
|
|
62
|
+
self.__generate_order(user_id=user_1, override_competed_date_utc=four_days_ago)
|
|
63
|
+
self.__generate_order(user_id=user_2)
|
|
64
|
+
self.__generate_order(user_id=user_3)
|
|
65
|
+
self.__generate_order(user_id=user_4)
|
|
66
|
+
|
|
67
|
+
self.__list_orders_from_known_id()
|
|
68
|
+
|
|
69
|
+
low = five_days_ago - timedelta(hours=1)
|
|
70
|
+
high = datetime.now(UTC)
|
|
71
|
+
print(f"checking for orders between {low} and {high} for {user_1}. Expecting 2")
|
|
72
|
+
self.__list_orders_for_user(
|
|
73
|
+
user_id=user_1, start_date_range=low, end_date_range=high
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
low = four_days_ago
|
|
77
|
+
high = low + timedelta(days=1)
|
|
78
|
+
print(
|
|
79
|
+
f"checking for orders between {low} and {high} for {user_1}. Expecting One"
|
|
80
|
+
)
|
|
81
|
+
self.__list_orders_for_user(
|
|
82
|
+
user_id=user_1, start_date_range=low, end_date_range=high
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def __generate_order(
|
|
86
|
+
self,
|
|
87
|
+
user_id: str = None,
|
|
88
|
+
override_competed_date_utc: datetime | None = None,
|
|
89
|
+
):
|
|
90
|
+
user_id = user_id or StringUtility.generate_uuid()
|
|
91
|
+
completed_date_utc = override_competed_date_utc or datetime.now(UTC)
|
|
92
|
+
order: Order = OrderService.new_order_object(user_id)
|
|
93
|
+
# technically we don't need to save this first
|
|
94
|
+
self.order_service.save(model=order)
|
|
95
|
+
# store the orders for later use
|
|
96
|
+
|
|
97
|
+
self.order_ids.append(order.id)
|
|
98
|
+
random_product_count = random.randint(1, 15)
|
|
99
|
+
for _ in range(random_product_count):
|
|
100
|
+
product: Product = self.__get_random_product()
|
|
101
|
+
order_item: OrderItem = OrderItem()
|
|
102
|
+
order_item.order_id = order.id
|
|
103
|
+
order_item.id = StringUtility.generate_uuid()
|
|
104
|
+
order_item.product = product
|
|
105
|
+
order_item.quantity = 1
|
|
106
|
+
|
|
107
|
+
self.order_item_service.save(model=order_item)
|
|
108
|
+
order.total += order_item.product.price * order_item.quantity
|
|
109
|
+
|
|
110
|
+
order.completed_utc = completed_date_utc
|
|
111
|
+
self.order_service.save(model=order)
|
|
112
|
+
|
|
113
|
+
def __get_random_product(self) -> Product:
|
|
114
|
+
"""Return a random product from the list"""
|
|
115
|
+
if not self.__products:
|
|
116
|
+
raise ValueError("No products available")
|
|
117
|
+
return random.choice(self.__products)
|
|
118
|
+
|
|
119
|
+
def __list_orders_from_known_id(self):
|
|
120
|
+
"""List the orders"""
|
|
121
|
+
print("######################################################")
|
|
122
|
+
print("Listing orders - looping through Order Ids")
|
|
123
|
+
for order_id in self.order_ids:
|
|
124
|
+
item: dict = self.order_service.get(
|
|
125
|
+
order_id=order_id, include_order_items=True
|
|
126
|
+
)
|
|
127
|
+
print(json.dumps(item, indent=2, cls=JsonEncoder))
|
|
128
|
+
|
|
129
|
+
print("End / Listing orders - looping through Order Ids")
|
|
130
|
+
|
|
131
|
+
def __list_orders_for_user(
|
|
132
|
+
self,
|
|
133
|
+
user_id: str,
|
|
134
|
+
start_date_range: datetime | None = None,
|
|
135
|
+
end_date_range: datetime | None = None,
|
|
136
|
+
):
|
|
137
|
+
"""List the orders for a user"""
|
|
138
|
+
print("######################################################")
|
|
139
|
+
print(f"Listing orders for user {user_id}")
|
|
140
|
+
items: list[dict] = self.order_service.list(
|
|
141
|
+
user_id=user_id, start_range=start_date_range, end_range=end_date_range
|
|
142
|
+
)
|
|
143
|
+
print(f"Found {len(items)} orders for user {user_id}")
|
|
144
|
+
# for item in items:
|
|
145
|
+
# print(json.dumps(item, indent=2, cls=JsonEncoder))
|
|
146
|
+
print("End / Listing orders for user")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def main():
|
|
150
|
+
"""Main"""
|
|
151
|
+
# get an environment file name or default to .env.docker
|
|
152
|
+
env_file_name: str = os.getenv("ENVRIONMENT_FILE", ".env.docker")
|
|
153
|
+
path = os.path.join(str(Path(__file__).parents[3].absolute()), env_file_name)
|
|
154
|
+
el: EnvironmentLoader = EnvironmentLoader()
|
|
155
|
+
if not os.path.exists(path=path):
|
|
156
|
+
raise FileNotFoundError("Failed to find the environmetn file")
|
|
157
|
+
loaded: bool = el.load_environment_file(path)
|
|
158
|
+
if not loaded:
|
|
159
|
+
raise RuntimeError("Failed to load my local environment")
|
|
160
|
+
|
|
161
|
+
table_name = "application_table"
|
|
162
|
+
example: DynamoDBExample = DynamoDBExample(table_name=table_name)
|
|
163
|
+
# load a single table design
|
|
164
|
+
example.run_examples()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
main()
|