Skip to content

Commit 8e49cbc

Browse files
committed
feat: ✨ add dynamoDB handler
1 parent 7edd96f commit 8e49cbc

File tree

5 files changed

+189
-0
lines changed

5 files changed

+189
-0
lines changed

src/dynamoDB/__init__.py

Whitespace-only changes.

src/dynamoDB/dynamoDBService.py

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import logging
2+
import time
3+
from typing import Any
4+
5+
import boto3
6+
from botocore.exceptions import ClientError
7+
8+
from src.utils.helper import get_iso_date_time_now
9+
from src.utils.types import IsoDateTimeString
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
class DynamoDBService:
15+
"""
16+
Handles read and write DynamoDB operations
17+
"""
18+
19+
def __init__(self, table_name: str, endpoint_url: str) -> None:
20+
self.dyn_resource = boto3.resource("dynamodb", endpoint_url=endpoint_url)
21+
self.table = self.get_table(table_name)
22+
23+
def get_table(self, table_name: str) -> Any:
24+
"""
25+
Returns a DynamoDB table
26+
"""
27+
if self.table_exists(table_name):
28+
table = self.dyn_resource.Table(table_name)
29+
else:
30+
table = self.create_table(table_name)
31+
return table
32+
33+
def table_exists(self, table_name) -> bool:
34+
"""
35+
Determines whether a table exists
36+
"""
37+
try:
38+
table = self.dyn_resource.Table(table_name)
39+
table.load()
40+
exists = True
41+
except ClientError as err:
42+
if err.response["Error"]["Code"] == "ResourceNotFoundException":
43+
exists = False
44+
else:
45+
logger.error(
46+
"Couldn't check for existence of %s. Here's why: %s: %s",
47+
table_name,
48+
err.response["Error"]["Code"],
49+
err.response["Error"]["Message"],
50+
)
51+
raise
52+
return exists
53+
54+
def create_table(self, table_name: str) -> Any:
55+
"""
56+
Creates a DynamoDB table
57+
"""
58+
try:
59+
table = self.dyn_resource.create_table(
60+
TableName=table_name,
61+
KeySchema=[
62+
{"AttributeName": "date_time", "KeyType": "HASH"},
63+
{"AttributeName": "image_name", "KeyType": "RANGE"},
64+
],
65+
AttributeDefinitions=[
66+
{"AttributeName": "date_time", "AttributeType": "S"},
67+
{"AttributeName": "image_name", "AttributeType": "S"},
68+
],
69+
ProvisionedThroughput={
70+
"ReadCapacityUnits": 5,
71+
"WriteCapacityUnits": 5,
72+
},
73+
)
74+
table.wait_until_exists()
75+
except ClientError as err:
76+
logger.error(
77+
"Couldn't create table %s. Here's why: %s: %s",
78+
table_name,
79+
err.response["Error"]["Code"],
80+
err.response["Error"]["Message"],
81+
)
82+
raise
83+
else:
84+
return table
85+
86+
def write_dockerhub_stats(
87+
self,
88+
user_name: str,
89+
image_name: str,
90+
pull_count: int,
91+
star_count: int,
92+
date_time: IsoDateTimeString,
93+
) -> None:
94+
"""
95+
Adds dockerhub stats to the DynamoDB table
96+
"""
97+
try:
98+
self.table.put_item(
99+
Item={
100+
"user_name": user_name,
101+
"image_name": image_name,
102+
"pull_count": pull_count,
103+
"star_count": star_count,
104+
"date_time": date_time,
105+
}
106+
)
107+
108+
except ClientError as err:
109+
logger.error(
110+
"Couldn't add dockerhubStats %s to table %s. Here's why: %s: %s",
111+
self.table.name,
112+
err.response["Error"]["Code"],
113+
err.response["Error"]["Message"],
114+
)
115+
raise
116+
117+
def read_dockerhub_stats(self, date_time: str, image_name: str) -> Any:
118+
"""
119+
Reads dockerhub stats from the DynamoDB table
120+
"""
121+
try:
122+
response = self.table.get_item(
123+
Key={"date_time": date_time, "image_name": image_name}
124+
)
125+
return response["Item"]
126+
127+
except ClientError as err:
128+
logger.error(
129+
"Couldn't read dockerhubStats %s from table %s. Here's why: %s: %s",
130+
self.table.name,
131+
err.response["Error"]["Code"],
132+
err.response["Error"]["Message"],
133+
)
134+
raise
135+
136+
def clean_up(self) -> None:
137+
"""
138+
Deletes the DynamoDB table
139+
"""
140+
try:
141+
self.table.delete()
142+
except ClientError as err:
143+
logger.error(
144+
"Couldn't delete table %s. Here's why: %s: %s",
145+
self.table.name,
146+
err.response["Error"]["Code"],
147+
err.response["Error"]["Message"],
148+
)
149+
raise
150+
151+
152+
if __name__ == "__main__":
153+
table_name = "dockerhubStats"
154+
user_name = "localstack"
155+
image_name = "localstack"
156+
pull_count = 100
157+
star_count = 100
158+
159+
endpoint_url = "http://localhost:8000"
160+
dynamoDBService = DynamoDBService(table_name, endpoint_url)
161+
162+
for _ in range(10):
163+
date_time = get_iso_date_time_now()
164+
dynamoDBService.write_dockerhub_stats(
165+
user_name, image_name, pull_count, star_count, date_time
166+
)
167+
time.sleep(1)
168+
169+
response = dynamoDBService.table.scan()
170+
171+
items = response.get("Items", [])
172+
for item in items:
173+
print(item)
174+
175+
dynamoDBService.clean_up()

src/utils/__init__.py

Whitespace-only changes.

src/utils/helper.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from datetime import datetime
2+
3+
from src.utils.types import IsoDateTimeString
4+
5+
6+
def get_iso_date_time_now() -> IsoDateTimeString:
7+
"""
8+
Returns the current date-time in ISO 8601 format
9+
"""
10+
return IsoDateTimeString(datetime.now().isoformat())

src/utils/types.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from typing import NewType
2+
3+
# Define a NewType for ISO 8601 date-time string
4+
IsoDateTimeString = NewType("IsoDateTimeString", str)

0 commit comments

Comments
 (0)