Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper dependency injection #98

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ testcontainers = "4.3.1"
profile = "black"

[tool.mypy]
packages= "src"
packages = "src"

[build-system]
requires = ["poetry-core"]
Expand Down
23 changes: 23 additions & 0 deletions src/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from functools import lru_cache
from typing import Annotated

from fastapi import Depends

from .services.member import MemberService
from .services.video import VideoService
from .settings import Settings


@lru_cache
def get_settings():
return Settings()


def get_member_service(settings: Annotated[Settings, Depends(get_settings)]):
"""Get the member service."""
return MemberService(settings.server_base_path)


def get_video_service(settings: Annotated[Settings, Depends(get_settings)]):
"""Get the video service."""
return VideoService(settings.server_base_path)
7 changes: 4 additions & 3 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@

from fastapi import FastAPI

from .dependencies import get_settings
from .routers import health, member, video
from .services.member import MemberService
from .services.video import VideoService

member_service = MemberService()
video_service = VideoService()


@asynccontextmanager
async def lifespan(api: FastAPI): # pylint: disable=unused-argument
"""Create the base paths for the video and member folders on startup."""
settings = get_settings()
member_service = MemberService(settings.server_base_path)
video_service = VideoService(settings.server_base_path)
video_service.create_base_path()
member_service.create_base_path()
yield
Expand Down
28 changes: 21 additions & 7 deletions src/routers/member.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
"""Member endpoints."""

import re
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, Response, UploadFile, status

from ..dependencies import get_member_service
from ..models.member import Member
from ..security import authorize
from ..services.member import MemberService

router = APIRouter(
tags=["Member"], prefix="/api/v1/member", dependencies=[Depends(authorize)]
)
service: MemberService = MemberService()


@router.post("", response_model=Member)
def create_member_folder(member: Member):
@router.post("")
def create_member_folder(
member: Member,
service: Annotated[MemberService, Depends(get_member_service)],
):
"""
Create a folder structure for a member and return the member object.
:param member: Member object
:param service: service dependency
:return: 200 and the original member object
"""
service.create_folder_structure(member)
return member


@router.put("", response_model=Member)
def update_member_folder(member: Member):
@router.put("")
def update_member_folder(
member: Member,
service: Annotated[MemberService, Depends(get_member_service)],
):
"""
Update the folder structure for a member and return the member object.
If the member does not exist, return a 404.
:param member: Member object
:param service: service dependency
:return: 200 and the original member object
"""
if not service.to_id_path(member.id).exists():
Expand All @@ -40,15 +49,20 @@ def update_member_folder(member: Member):
return member


@router.post("/{member_id}/profilePicture", response_model=UUID)
async def upload_member_picture(member_id: UUID, file: UploadFile):
@router.post("/{member_id}/profilePicture")
async def upload_member_picture(
member_id: UUID,
file: UploadFile,
service: Annotated[MemberService, Depends(get_member_service)],
):
"""
Upload a picture for a member to convert
and store the profile picture in different formats
If the member does not exist, return a 404.
If the file is not an image, return a 500.
:param member_id: the id of the member
:param file: the image file
:param service: service dependency
:return: 200 and the original member_id
"""
# pylint: disable=duplicate-code
Expand Down
23 changes: 17 additions & 6 deletions src/routers/video.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
"""Video endpoints"""

import re
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, Response, UploadFile, status

from ..dependencies import get_video_service
from ..models.video import Video
from ..security import authorize
from ..services.video import VideoService

router = APIRouter(
tags=["Video"], prefix="/api/v1/video", dependencies=[Depends(authorize)]
)
service: VideoService = VideoService()


@router.post("", response_model=Video)
def create_video_folder(video: Video):
def create_video_folder(
video: Video,
service: Annotated[VideoService, Depends(get_video_service)],
):
"""
Create a folder structure for a video and return the video object.
:param video: Video object
Expand All @@ -26,8 +30,11 @@ def create_video_folder(video: Video):
return video


@router.put("", response_model=Video)
def update_video_folder(video: Video):
@router.put("")
def update_video_folder(
video: Video,
service: Annotated[VideoService, Depends(get_video_service)],
):
"""
Update the folder structure for a video and return the video object.
If the video does not exist, return a 404.
Expand All @@ -40,8 +47,12 @@ def update_video_folder(video: Video):
return video


@router.post("/{video_id}/thumbnail", response_model=UUID)
async def upload_video_poster(video_id: UUID, file: UploadFile):
@router.post("/{video_id}/thumbnail")
async def upload_video_poster(
video_id: UUID,
file: UploadFile,
service: Annotated[VideoService, Depends(get_video_service)],
):
"""
Upload a picture for a video thumbnail to convert
and store the thumbnail in different formats
Expand Down
15 changes: 7 additions & 8 deletions src/services/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
from uuid import UUID

from ..models.member import Member
from ..settings import settings
from .image import ImgFormat, create_images


class MemberService:
"""Member service class."""

def __init__(self, base_path: str = settings.server_base_path):
def __init__(self, base_path: str):
self.id_paths_base = Path(base_path, "m")
self.url_paths_base = Path(base_path, "member")
self.profile_picture_sizes = [
ImgFormat(1920, 1080, "xl"),
ImgFormat(1280, 720, "l"),
ImgFormat(854, 480, "m"),
]

def create_folder_structure(self, member: Member):
"""
Expand All @@ -39,12 +43,7 @@ def create_profile_picture(self, img_file: bytes, member_id: UUID):
:return: None
"""
profile_picture_path = Path(self.to_id_path(member_id), "profile")
profile_picture_sizes = [
ImgFormat(1920, 1080, "xl"),
ImgFormat(1280, 720, "l"),
ImgFormat(854, 480, "m"),
]
create_images(img_file, profile_picture_path, profile_picture_sizes)
create_images(img_file, profile_picture_path, self.profile_picture_sizes)

def update_symlink(self, member: Member):
"""
Expand Down
15 changes: 7 additions & 8 deletions src/services/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
from uuid import UUID

from ..models.video import Video
from ..settings import settings
from .image import ImgFormat, create_images


class VideoService:
"""Video service class."""

def __init__(self, base_path: str = settings.server_base_path):
def __init__(self, base_path: str):
self.id_paths_base = Path(base_path, "v")
self.url_paths_base = Path(base_path, "video")
self.poster_sizes = [
ImgFormat(1920, 1080, "fhd"),
ImgFormat(1280, 720, "hd"),
ImgFormat(854, 480, "sd"),
]

def create_folder_structure(self, video: Video):
"""
Expand Down Expand Up @@ -41,12 +45,7 @@ def create_thumbnails(self, img_file: bytes, video_id: UUID):
:return: None
"""
thumbnail_path = Path(self.to_id_path(video_id), "thumbnail")
poster_sizes = [
ImgFormat(1920, 1080, "fhd"),
ImgFormat(1280, 720, "hd"),
ImgFormat(854, 480, "sd"),
]
create_images(img_file, thumbnail_path, poster_sizes)
create_images(img_file, thumbnail_path, self.poster_sizes)

def update_symlinks(self, video: Video):
"""
Expand Down