Skip to content

Commit

Permalink
Add AWS S3 Service (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
ltan02 authored Nov 28, 2023
1 parent 4c0d94d commit 51a795d
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 40 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ jobs:
shell: bash
working-directory: ./backend

env:
SECRET_KEY: ${{ secrets.DJANGO_SECRET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_STORAGE_BUCKET_NAME: ${{ secrets.AWS_STORAGE_BUCKET_NAME }}
AWS_S3_REGION_NAME: ${{ secrets.AWS_S3_REGION_NAME }}

steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -42,13 +49,9 @@ jobs:
pipenv run flake8
- name: Makes sure it runs
env:
SECRET_KEY: ${{ secrets.DJANGO_SECRET }}
run: |
pipenv run python manage.py check
- name: Run tests
env:
SECRET_KEY: ${{ secrets.DJANGO_SECRET }}
run: |
pipenv run python manage.py test
2 changes: 2 additions & 0 deletions backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ django = ">=4.2.6"
djangorestframework = ">=3.14.0"
django-environ = ">=0.11.2"
psycopg2 = ">=2.9.9"
boto3 = ">=1.29.6"
django-storages = ">=1.14.2"

[dev-packages]
flake8 = ">=6.1.0"
Expand Down
120 changes: 94 additions & 26 deletions backend/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions backend/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,23 @@
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"storages",
"core",
"auction",
"user",
"vehicle",
"bid",
]

# AWS S3 Configuration
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME")
AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME")
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"

DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
Expand Down
7 changes: 6 additions & 1 deletion backend/sample.env
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
SECRET_KEY=
SECRET_KEY=
ENVIRONMENT=dev
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_STORAGE_BUCKET_NAME=
AWS_S3_REGION_NAME=
85 changes: 85 additions & 0 deletions backend/services/AWSS3Service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import boto3
from botocore.exceptions import ClientError, NoCredentialsError
from django.conf import settings


class AWSS3Service:
EXCEL_FILE_PATH = "inventory-excels/"
IMAGE_FILE_PATH = "vehicle-images/"

def __init__(self):
self.s3_client = boto3.client(
"s3",
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_S3_REGION_NAME,
)
self.bucket_name = settings.AWS_STORAGE_BUCKET_NAME

def __upload_file(self, file_path, file_name, file_content):
try:
self.s3_client.put_object(
Bucket=self.bucket_name,
Key=f"{file_path}{file_name}",
Body=file_content,
)
return True
except NoCredentialsError:
print("Credentials not available")
return False

def __get_file(self, file_path, file_name):
try:
response = self.s3_client.get_object(
Bucket=self.bucket_name, Key=f"{file_path}{file_name}"
)
return response["Body"].read()
except ClientError as e:
print(f"Error getting file: {e}")
return None

def __delete_file(self, file_path, file_name):
try:
self.s3_client.delete_object(
Bucket=self.bucket_name, Key=f"{file_path}{file_name}"
)
return True
except ClientError as e:
print(f"Error deleting file: {e}")
return False

def upload_image(self, vehicle_id, file_name, file_content):
vehicle_folder_path = f"{self.IMAGE_FILE_PATH}{vehicle_id}/"
return self.__upload_file(vehicle_folder_path, file_name, file_content)

def upload_excel_file(self, file_name, file_content):
return self.__upload_file(self.EXCEL_FILE_PATH, file_name, file_content)

def get_latest_excel_file(self):
try:
response = self.s3_client.list_objects_v2(
Bucket=self.bucket_name, Prefix=self.EXCEL_FILE_PATH
)

if "Contents" in response:
excel_files = filter(
lambda x: x["Key"] != self.EXCEL_FILE_PATH, response["Contents"]
)

latest_file_key = sorted(
excel_files, key=lambda x: x["LastModified"], reverse=True
)[0]["Key"]

return self.__get_file("", latest_file_key)
else:
return None
except ClientError as e:
print(f"Error getting latest Excel file: {e}")
return None

def delete_image(self, vehicle_id, file_name):
vehicle_folder_path = f"{self.IMAGE_FILE_PATH}{vehicle_id}/"
return self.__delete_file(vehicle_folder_path, file_name)

def delete_excel_file(self, file_name):
return self.__delete_file(self.EXCEL_FILE_PATH, file_name)
Empty file added backend/services/__init__.py
Empty file.
11 changes: 2 additions & 9 deletions backend/vehicle/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import (
Brand, Equipment, Supplier, Trailer, Type, UnitImage, Vehicle)
from .serializers import (
BrandSerializer, EquipmentSerializer, SupplierSerializer,
TrailerSerializer, TypeSerializer, UnitImageSerializer, VehicleSerializer)
from .models import Vehicle
from .serializers import VehicleSerializer


# Create your views here.
Expand Down Expand Up @@ -70,9 +67,5 @@ def get(self, request, vehicle_id, *args, **kwargs):
vehicle = get_object_or_404(Vehicle, id=vehicle_id)
serializer = VehicleSerializer(vehicle)
return Response(serializer.data, status=status.HTTP_200_OK)
# identifier = request.data.get("id")
# vehicle = Vehicle.objects.get(id=identifier)
# serialized_data = self.serializer_class(vehicle)
# return Response(serialized_data.data, status=status.HTTP_200_OK)
except vehicle.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

0 comments on commit 51a795d

Please sign in to comment.