-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactoring of backend: implementing a layered architecture
- Loading branch information
Showing
17 changed files
with
205 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from typing import List, Optional | ||
from sqlalchemy.orm import Session | ||
from sqlalchemy import select, update | ||
from dataclasses import dataclass | ||
from .models import Task, TaskStatus | ||
from .schema import TaskInput, TaskOutput | ||
|
||
|
||
@dataclass | ||
class TaskRepository: | ||
session: Session | ||
current_user_id: int | ||
|
||
def create(self, data: TaskInput) -> TaskOutput: | ||
task = Task(**data.model_dump(exclude_none=True)) | ||
self.session.add(task) | ||
self.session.commit() | ||
self.session.refresh(task) | ||
return TaskOutput(**dict(task)) | ||
|
||
def list(self, offset: int, limit: int) -> List[Optional[TaskOutput]]: | ||
stmt = ( | ||
select(Task) | ||
.where( | ||
Task.status != TaskStatus.deleted, | ||
Task.user_id == self.current_user_id, | ||
) | ||
.offset(offset) | ||
.limit(limit) | ||
) | ||
tasks = self.session.execute(stmt).all() | ||
return [TaskOutput(**dict(task[0])) for task in tasks] | ||
|
||
def get_by_id(self, id: int) -> Task: | ||
return self.session.get(Task, id) | ||
|
||
def update(self, task: Task) -> TaskOutput: | ||
self.session.execute( | ||
update(Task).where(Task.id == task.id).values(**dict(task)) | ||
) | ||
self.session.commit() | ||
self.session.refresh(task) | ||
return TaskOutput(**dict(task)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from typing import Annotated | ||
from fastapi import Depends, Query, APIRouter | ||
from sqlalchemy.orm import Session | ||
from app.users.models import User | ||
from app.auth import get_current_active_user | ||
from app.db import get_session | ||
from .schema import TaskInput, TaskOutput | ||
from .service import TaskService | ||
|
||
|
||
router = APIRouter(prefix="/tasks") | ||
|
||
|
||
@router.get("/") | ||
def read_tasks( | ||
*, | ||
current_user: Annotated[User, Depends(get_current_active_user)], | ||
session: Session = Depends(get_session), | ||
offset: int = 0, | ||
limit: int = Query(default=100, le=100), | ||
): | ||
return TaskService(session, current_user.id).list(offset, limit) | ||
|
||
|
||
@router.post("/", response_model=TaskOutput) | ||
def create_task( | ||
*, | ||
current_user: Annotated[User, Depends(get_current_active_user)], | ||
session: Session = Depends(get_session), | ||
data: TaskInput, | ||
): | ||
return TaskService(session, current_user.id).create(data) | ||
|
||
|
||
@router.get("/{task_id}") | ||
def read_task( | ||
*, | ||
session: Session = Depends(get_session), | ||
current_user: Annotated[User, Depends(get_current_active_user)], | ||
task_id: int, | ||
): | ||
return TaskService(session, current_user.id).read(task_id) | ||
|
||
|
||
@router.patch("/{task_id}") | ||
def update_task( | ||
*, | ||
session: Session = Depends(get_session), | ||
current_user: Annotated[User, Depends(get_current_active_user)], | ||
task_id: int, | ||
task: TaskInput, | ||
): | ||
return TaskService(session, current_user.id).update(task_id, task) | ||
|
||
|
||
@router.delete("/{task_id}") | ||
def delete_task( | ||
*, | ||
current_user: Annotated[User, Depends(get_current_active_user)], | ||
session: Session = Depends(get_session), | ||
task_id: int, | ||
): | ||
return TaskService(session, current_user.id).delete(task_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import datetime | ||
from typing import Optional | ||
from pydantic import BaseModel, Field | ||
from .models import TaskStatus | ||
|
||
|
||
class TaskInput(BaseModel): | ||
title: str = Field(min_length=1, max_length=50) | ||
description: str | None = None | ||
user_id: int | None = None | ||
due_date: datetime.datetime | None = None | ||
status: TaskStatus = TaskStatus.created | ||
|
||
|
||
class TaskOutput(BaseModel): | ||
id: int | ||
title: str | ||
description: Optional[str] = "" | ||
user_id: int | ||
due_date: datetime.datetime | None = None | ||
status: TaskStatus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from typing import List | ||
from sqlalchemy.orm import Session | ||
from dataclasses import dataclass | ||
from fastapi import HTTPException | ||
from .repository import TaskRepository | ||
from .models import TaskStatus, Task | ||
from .schema import TaskInput, TaskOutput | ||
|
||
|
||
@dataclass | ||
class TaskService: | ||
session: Session | ||
current_user_id: int | ||
|
||
def __post_init__(self): | ||
self.repository: TaskRepository = TaskRepository( | ||
self.session, self.current_user_id | ||
) | ||
|
||
def get_or_not_found(self, task_id: int) -> Task: | ||
task = self.repository.get_by_id(task_id) | ||
if (not task) or (task.user_id != self.current_user_id): | ||
raise HTTPException(status_code=404, detail="Task not found") | ||
return task | ||
|
||
def list(self, offset: int, limit: int) -> List[TaskOutput]: | ||
return self.repository.list(offset, limit) | ||
|
||
def create(self, data: TaskInput) -> TaskOutput: | ||
data.user_id = self.current_user_id | ||
return self.repository.create(data) | ||
|
||
def read(self, task_id: int) -> TaskOutput: | ||
task = self.get_or_not_found(task_id) | ||
return TaskOutput(**dict(task)) | ||
|
||
def update(self, task_id: int, data: TaskInput) -> TaskOutput: | ||
task = self.get_or_not_found(task_id) | ||
task.title = data.title | ||
task.description = data.description | ||
task.due_date = data.due_date | ||
|
||
task.user_id = self.current_user_id | ||
self.repository.update(task) | ||
return TaskOutput(**dict(task)) | ||
|
||
def delete(self, task_id: int) -> TaskOutput: | ||
task = self.get_or_not_found(task_id) | ||
|
||
task.status = TaskStatus.deleted | ||
self.repository.update(task) | ||
return task |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from dataclasses import dataclass | ||
from sqlalchemy.orm import Session | ||
|
||
|
||
@dataclass | ||
class UserRepository: | ||
session: Session |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.