Skip to content

Commit 5cc0889

Browse files
✨ Refactor item management to document management in API and models
1 parent ac60cae commit 5cc0889

File tree

7 files changed

+148
-23
lines changed

7 files changed

+148
-23
lines changed

backend/app/api/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from fastapi import APIRouter
22

3-
from app.api.routes import items, login, private, users, utils
3+
from app.api.routes import documents, login, private, users, utils
44
from app.core.config import settings
55

66
api_router = APIRouter()
77
api_router.include_router(login.router)
88
api_router.include_router(users.router)
99
api_router.include_router(utils.router)
10-
api_router.include_router(items.router)
10+
api_router.include_router(documents.router)
1111

1212

1313
if settings.ENVIRONMENT == "local":
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import uuid
2+
from typing import Any
3+
4+
from fastapi import APIRouter, HTTPException
5+
from sqlmodel import func, select
6+
7+
from app.api.deps import CurrentUser, SessionDep
8+
from app.models import Document, DocumentCreate, DocumentPublic, DocumentsPublic, DocumentUpdate, Message
9+
10+
router = APIRouter(prefix="/documents", tags=["documents"])
11+
12+
13+
@router.get("/", response_model=DocumentsPublic)
14+
def read_documents(
15+
session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
16+
) -> Any:
17+
"""
18+
Retrieve documents.
19+
"""
20+
21+
if current_user.is_superuser:
22+
count_statement = select(func.count()).select_from(Document)
23+
count = session.exec(count_statement).one()
24+
statement = select(Document).offset(skip).limit(limit)
25+
documents = session.exec(statement).all()
26+
else:
27+
count_statement = (
28+
select(func.count())
29+
.select_from(Document)
30+
.where(Document.owner_id == current_user.id)
31+
)
32+
count = session.exec(count_statement).one()
33+
statement = (
34+
select(Document)
35+
.where(Document.owner_id == current_user.id)
36+
.offset(skip)
37+
.limit(limit)
38+
)
39+
documents = session.exec(statement).all()
40+
41+
return DocumentsPublic(data=documents, count=count)
42+
43+
44+
@router.get("/{id}", response_model=DocumentPublic)
45+
def read_document(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
46+
"""
47+
Get document by ID.
48+
"""
49+
document = session.get(Document, id)
50+
if not document:
51+
raise HTTPException(status_code=404, detail="Document not found")
52+
if not current_user.is_superuser and (document.owner_id != current_user.id):
53+
raise HTTPException(status_code=400, detail="Not enough permissions")
54+
return document
55+
56+
57+
@router.post("/", response_model=DocumentPublic)
58+
def create_document(
59+
*, session: SessionDep, current_user: CurrentUser, document_in: DocumentCreate
60+
) -> Any:
61+
"""
62+
Create new document.
63+
"""
64+
document = Document.model_validate(document_in, update={"owner_id": current_user.id})
65+
session.add(document)
66+
session.commit()
67+
session.refresh(document)
68+
return document
69+
70+
71+
@router.put("/{id}", response_model=DocumentPublic)
72+
def update_document(
73+
*,
74+
session: SessionDep,
75+
current_user: CurrentUser,
76+
id: uuid.UUID,
77+
document_in: DocumentUpdate,
78+
) -> Any:
79+
"""
80+
Update an document.
81+
"""
82+
document = session.get(Document, id)
83+
if not document:
84+
raise HTTPException(status_code=404, detail="Document not found")
85+
if not current_user.is_superuser and (document.owner_id != current_user.id):
86+
raise HTTPException(status_code=400, detail="Not enough permissions")
87+
update_dict = document_in.model_dump(exclude_unset=True)
88+
document.sqlmodel_update(update_dict)
89+
session.add(document)
90+
session.commit()
91+
session.refresh(document)
92+
return document
93+
94+
95+
@router.delete("/{id}")
96+
def delete_document(
97+
session: SessionDep, current_user: CurrentUser, id: uuid.UUID
98+
) -> Message:
99+
"""
100+
Delete an document.
101+
"""
102+
document = session.get(Document, id)
103+
if not document:
104+
raise HTTPException(status_code=404, detail="Document not found")
105+
if not current_user.is_superuser and (document.owner_id != current_user.id):
106+
raise HTTPException(status_code=400, detail="Not enough permissions")
107+
session.delete(document)
108+
session.commit()
109+
return Message(message="Document deleted successfully")

backend/app/api/routes/users.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from app.core.config import settings
1414
from app.core.security import get_password_hash, verify_password
1515
from app.models import (
16-
Item,
16+
Document,
1717
Message,
1818
UpdatePassword,
1919
User,
@@ -219,7 +219,7 @@ def delete_user(
219219
raise HTTPException(
220220
status_code=403, detail="Super users are not allowed to delete themselves"
221221
)
222-
statement = delete(Item).where(col(Item.owner_id) == user_id)
222+
statement = delete(Document).where(col(Document.owner_id) == user_id)
223223
session.exec(statement) # type: ignore
224224
session.delete(user)
225225
session.commit()

backend/app/crud.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sqlmodel import Session, select
55

66
from app.core.security import get_password_hash, verify_password
7-
from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
7+
from app.models import Document, DocumentCreate, User, UserCreate, UserUpdate
88

99

1010
def create_user(*, session: Session, user_create: UserCreate) -> User:
@@ -46,9 +46,9 @@ def authenticate(*, session: Session, email: str, password: str) -> User | None:
4646
return db_user
4747

4848

49-
def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item:
50-
db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
51-
session.add(db_item)
49+
def create_document(*, session: Session, document_in: DocumentCreate, owner_id: uuid.UUID) -> Document:
50+
db_document = Document.model_validate(document_in, update={"owner_id": owner_id})
51+
session.add(db_document)
5252
session.commit()
53-
session.refresh(db_item)
54-
return db_item
53+
session.refresh(db_document)
54+
return db_document

backend/app/models.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class UpdatePassword(SQLModel):
4343
class User(UserBase, table=True):
4444
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
4545
hashed_password: str
46-
items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True)
46+
documents: list["Document"] = Relationship(back_populates="owner", cascade_delete=True)
4747

4848

4949
# Properties to return via API, id is always required
@@ -57,38 +57,38 @@ class UsersPublic(SQLModel):
5757

5858

5959
# Shared properties
60-
class ItemBase(SQLModel):
60+
class DocumentBase(SQLModel):
6161
title: str = Field(min_length=1, max_length=255)
6262
description: str | None = Field(default=None, max_length=255)
6363

6464

65-
# Properties to receive on item creation
66-
class ItemCreate(ItemBase):
65+
# Properties to receive on document creation
66+
class DocumentCreate(DocumentBase):
6767
pass
6868

6969

70-
# Properties to receive on item update
71-
class ItemUpdate(ItemBase):
70+
# Properties to receive on document update
71+
class DocumentUpdate(DocumentBase):
7272
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
7373

7474

7575
# Database model, database table inferred from class name
76-
class Item(ItemBase, table=True):
76+
class Document(DocumentBase, table=True):
7777
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
7878
owner_id: uuid.UUID = Field(
7979
foreign_key="user.id", nullable=False, ondelete="CASCADE"
8080
)
81-
owner: User | None = Relationship(back_populates="items")
81+
owner: User | None = Relationship(back_populates="documents")
8282

8383

8484
# Properties to return via API, id is always required
85-
class ItemPublic(ItemBase):
85+
class DocumentPublic(DocumentBase):
8686
id: uuid.UUID
8787
owner_id: uuid.UUID
8888

8989

90-
class ItemsPublic(SQLModel):
91-
data: list[ItemPublic]
90+
class DocumentsPublic(SQLModel):
91+
data: list[DocumentPublic]
9292
count: int
9393

9494

backend/app/tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from app.core.config import settings
88
from app.core.db import engine, init_db
99
from app.main import app
10-
from app.models import Item, User
10+
from app.models import Document, User
1111
from app.tests.utils.user import authentication_token_from_email
1212
from app.tests.utils.utils import get_superuser_token_headers
1313

@@ -17,7 +17,7 @@ def db() -> Generator[Session, None, None]:
1717
with Session(engine) as session:
1818
init_db(session)
1919
yield session
20-
statement = delete(Item)
20+
statement = delete(Document)
2121
session.execute(statement)
2222
statement = delete(User)
2323
session.execute(statement)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from sqlmodel import Session
2+
3+
from app import crud
4+
from app.models import Document, DocumentCreate
5+
from app.tests.utils.user import create_random_user
6+
from app.tests.utils.utils import random_lower_string
7+
8+
9+
def create_random_document(db: Session) -> Document:
10+
user = create_random_user(db)
11+
owner_id = user.id
12+
assert owner_id is not None
13+
title = random_lower_string()
14+
description = random_lower_string()
15+
document_in = DocumentCreate(title=title, description=description)
16+
return crud.create_document(session=db, document_in=document_in, owner_id=owner_id)

0 commit comments

Comments
 (0)