Skip to content

Commit ad3363a

Browse files
committed
Initial optimisation
1 parent 5ab7e1e commit ad3363a

22 files changed

Lines changed: 269 additions & 387 deletions

File tree

apps/backend/app/api/routes/code_snippets/api.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
from typing import Optional
21

32
from fastapi import APIRouter, Depends, Query
43

54
from app.api.routes.auth.services import get_current_uid
6-
from app.api.routes.code_snippets.schema import CodeSnippetCreate, CodeSnippetOut, CodeSnippetUpdate
75
from app.api.routes.code_snippets import services as snippet_svc
6+
from app.api.routes.code_snippets.schema import CodeSnippetCreate, CodeSnippetOut, CodeSnippetUpdate
87

98
router = APIRouter(prefix="/code-snippets", tags=["code-snippets"])
109

@@ -13,7 +12,7 @@
1312
async def list_snippets(
1413
uid: str = Depends(get_current_uid),
1514
skip: int = Query(default=0, ge=0),
16-
limit: Optional[int] = Query(default=None, ge=1, le=500),
15+
limit: int | None = Query(default=None, ge=1, le=500),
1716
) -> list[CodeSnippetOut]:
1817
return await snippet_svc.list_code_snippets(uid=uid, skip=skip, limit=limit)
1918

apps/backend/app/api/routes/code_snippets/schema.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
db.code_snippets.create_index([("created_by", 1), ("updatedAt", -1)])
1010
"""
1111

12-
from typing import Optional
1312

1413
from pydantic import BaseModel, ConfigDict, Field
1514

@@ -21,24 +20,24 @@
2120
class CodeSnippetCreate(BaseModel):
2221
model_config = ConfigDict(extra="ignore")
2322

24-
id: Optional[str] = Field(default=None, description="Client-generated id (UUID); optional")
23+
id: str | None = Field(default=None, description="Client-generated id (UUID); optional")
2524
title: str = Field(default="Untitled snippet", min_length=1)
2625
language: str = Field(default="auto", min_length=1)
2726
code: str = ""
28-
tags: Optional[list[str]] = None
29-
pinned: Optional[bool] = None
30-
createdAt: Optional[int] = Field(default=None, description="Unix ms; server defaults if omitted")
31-
updatedAt: Optional[int] = Field(default=None, description="Unix ms; server defaults if omitted")
27+
tags: list[str] | None = None
28+
pinned: bool | None = None
29+
createdAt: int | None = Field(default=None, description="Unix ms; server defaults if omitted")
30+
updatedAt: int | None = Field(default=None, description="Unix ms; server defaults if omitted")
3231

3332

3433
class CodeSnippetUpdate(BaseModel):
3534
model_config = ConfigDict(extra="ignore")
3635

37-
title: Optional[str] = Field(default=None, min_length=1)
38-
language: Optional[str] = Field(default=None, min_length=1)
39-
code: Optional[str] = None
40-
tags: Optional[list[str]] = None
41-
pinned: Optional[bool] = None
36+
title: str | None = Field(default=None, min_length=1)
37+
language: str | None = Field(default=None, min_length=1)
38+
code: str | None = None
39+
tags: list[str] | None = None
40+
pinned: bool | None = None
4241

4342

4443
class CodeSnippetOut(BaseModel):

apps/backend/app/api/routes/code_snippets/services.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
from typing import Any
22

33
from fastapi import HTTPException, status
4-
from pymongo.errors import PyMongoError
5-
from pymongo import ReturnDocument
64

7-
from app.utils.collection_name import CODE_SNIPPETS as SNIPPETS
85
from app.api.routes.code_snippets.schema import (
96
CodeSnippetCreate,
107
CodeSnippetOut,
118
CodeSnippetUpdate,
129
)
10+
from app.core.cache import bump_version, cached
1311
from app.database import db_manager
14-
from app.utils.utils import create_timestamp, is_duplicate_key_error, new_id
15-
from app.core.cache import cached, bump_version
12+
from app.utils.collection_name import CODE_SNIPPETS as SNIPPETS
13+
from app.utils.crud import safe_delete_one, safe_insert, safe_update_one
14+
from app.utils.utils import create_timestamp, new_id
1615

1716

1817
def _doc_to_out(doc: dict[str, Any]) -> CodeSnippetOut:
@@ -57,18 +56,7 @@ async def create_code_snippet(uid: str, body: CodeSnippetCreate) -> CodeSnippetO
5756
"createdAt": created,
5857
"updatedAt": updated,
5958
}
60-
try:
61-
await db_manager.insert_one(SNIPPETS, doc)
62-
except PyMongoError as exc:
63-
if is_duplicate_key_error(exc):
64-
raise HTTPException(
65-
status_code=status.HTTP_409_CONFLICT,
66-
detail="Snippet id already exists.",
67-
) from exc
68-
raise HTTPException(
69-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
70-
detail="Failed to create snippet.",
71-
) from exc
59+
await safe_insert(SNIPPETS, doc, name="Snippet")
7260
await bump_version(ns="code_snippets", uid=uid)
7361
return _doc_to_out(doc)
7462

@@ -78,20 +66,12 @@ async def update_code_snippet(uid: str, snippet_id: str, body: CodeSnippetUpdate
7866
if not patch:
7967
return await get_code_snippet(uid=uid, snippet_id=snippet_id)
8068
patch["updatedAt"] = create_timestamp()
81-
try:
82-
result = await db_manager.find_one_and_update(
83-
SNIPPETS,
84-
{"_id": snippet_id, "created_by": uid},
85-
{"$set": patch},
86-
return_document=ReturnDocument.AFTER,
87-
)
88-
except PyMongoError as exc:
89-
raise HTTPException(
90-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
91-
detail="Failed to update snippet.",
92-
) from exc
93-
if not result:
94-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Snippet not found.")
69+
result = await safe_update_one(
70+
SNIPPETS,
71+
{"_id": snippet_id, "created_by": uid},
72+
patch,
73+
name="Snippet",
74+
)
9575
await bump_version(ns="code_snippets", uid=uid)
9676
return _doc_to_out(result)
9777

@@ -105,7 +85,5 @@ async def get_code_snippet(*, uid: str, snippet_id: str) -> CodeSnippetOut:
10585

10686

10787
async def delete_code_snippet(uid: str, snippet_id: str) -> None:
108-
result = await db_manager.delete_one(SNIPPETS, {"_id": snippet_id, "created_by": uid})
109-
if result.deleted_count == 0:
110-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Snippet not found.")
88+
await safe_delete_one(SNIPPETS, {"_id": snippet_id, "created_by": uid}, name="Snippet")
11189
await bump_version(ns="code_snippets", uid=uid)

apps/backend/app/api/routes/environment_manager/api.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
from typing import Optional
21

32
from fastapi import APIRouter, Depends, Query
43

54
from app.api.routes.auth.services import get_current_uid
5+
from app.api.routes.environment_manager import services as env_svc
66
from app.api.routes.environment_manager.schema import (
77
EnvSetEntryCreate,
88
EnvSetEntryOut,
99
EnvSetEntryUpdate,
1010
)
11-
from app.api.routes.environment_manager import services as env_svc
1211

1312
router = APIRouter(prefix="/environment-manager", tags=["environment-manager"])
1413

@@ -20,7 +19,7 @@
2019
)
2120
async def list_entries(
2221
uid: str = Depends(get_current_uid),
23-
limit: Optional[int] = Query(default=None, ge=1, le=1000),
22+
limit: int | None = Query(default=None, ge=1, le=1000),
2423
offset: int = Query(default=0, ge=0),
2524
) -> list[EnvSetEntryOut]:
2625
return await env_svc.list_entries(uid, limit=limit, offset=offset)

apps/backend/app/api/routes/environment_manager/schema.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
from typing import Optional
21

32
from pydantic import BaseModel, ConfigDict, Field
43

54

65
class EnvSetEntryCreate(BaseModel):
76
encryptedData: str = Field(min_length=1)
87
iv: str = Field(min_length=1)
9-
createdAt: Optional[int] = Field(default=None, ge=0)
10-
updatedAt: Optional[int] = Field(default=None, ge=0)
8+
createdAt: int | None = Field(default=None, ge=0)
9+
updatedAt: int | None = Field(default=None, ge=0)
1110

1211

1312
class EnvSetEntryUpdate(BaseModel):
1413
model_config = ConfigDict(extra="ignore")
1514

1615
encryptedData: str = Field(min_length=1)
1716
iv: str = Field(min_length=1)
18-
updatedAt: Optional[int] = Field(default=None, ge=0)
17+
updatedAt: int | None = Field(default=None, ge=0)
1918

2019

2120
class EnvSetEntryOut(BaseModel):

apps/backend/app/api/routes/environment_manager/services.py

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
from typing import Any, Optional
1+
from typing import Any
22

33
from fastapi import HTTPException, status
4-
from pymongo.errors import PyMongoError
54

65
from app.api.routes.environment_manager.schema import (
76
EnvSetEntryCreate,
87
EnvSetEntryOut,
98
EnvSetEntryUpdate,
109
)
11-
from app.utils.collection_name import ENV_MANAGER_ENTRIES
12-
from app.utils.utils import create_timestamp, is_duplicate_key_error, new_id
1310
from app.database import db_manager
11+
from app.utils.collection_name import ENV_MANAGER_ENTRIES
12+
from app.utils.crud import safe_delete_one, safe_insert, safe_update_one
13+
from app.utils.utils import create_timestamp, new_id
1414

1515

1616
def _entry_doc_to_out(doc: dict[str, Any], *, entry_id: str) -> EnvSetEntryOut:
@@ -25,7 +25,7 @@ def _entry_doc_to_out(doc: dict[str, Any], *, entry_id: str) -> EnvSetEntryOut:
2525
)
2626

2727

28-
async def list_entries(uid: str, *, limit: Optional[int] = None, offset: int = 0) -> list[EnvSetEntryOut]:
28+
async def list_entries(uid: str, *, limit: int | None = None, offset: int = 0) -> list[EnvSetEntryOut]:
2929
docs = await db_manager.find(
3030
ENV_MANAGER_ENTRIES,
3131
{"created_by": uid},
@@ -50,15 +50,7 @@ async def create_entry(uid: str, body: EnvSetEntryCreate) -> EnvSetEntryOut:
5050
"createdAt": created_at,
5151
"updatedAt": updated_at,
5252
}
53-
try:
54-
await db_manager.insert_one(ENV_MANAGER_ENTRIES, doc)
55-
except PyMongoError as exc:
56-
if is_duplicate_key_error(exc):
57-
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Entry id collision.") from exc
58-
raise HTTPException(
59-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to create entry."
60-
) from exc
61-
53+
await safe_insert(ENV_MANAGER_ENTRIES, doc, name="Entry")
6254
return _entry_doc_to_out(doc, entry_id=eid)
6355

6456

@@ -76,21 +68,16 @@ async def update_entry(uid: str, entry_id: str, body: EnvSetEntryUpdate) -> EnvS
7668
"iv": body.iv,
7769
"updatedAt": ts_updated,
7870
}
79-
try:
80-
result = await db_manager.update_one(
81-
ENV_MANAGER_ENTRIES, {"_id": entry_id, "created_by": uid}, {"$set": patch}
82-
)
83-
except PyMongoError as exc:
84-
raise HTTPException(
85-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to update entry."
86-
) from exc
87-
88-
if result.matched_count == 0:
89-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Entry not found.")
90-
return await get_entry(uid, entry_id)
71+
result = await safe_update_one(
72+
ENV_MANAGER_ENTRIES,
73+
{"_id": entry_id, "created_by": uid},
74+
patch,
75+
name="Entry",
76+
)
77+
return _entry_doc_to_out(result, entry_id=entry_id)
9178

9279

9380
async def delete_entry(uid: str, entry_id: str) -> None:
94-
result = await db_manager.delete_one(ENV_MANAGER_ENTRIES, {"_id": entry_id, "created_by": uid})
95-
if result.deleted_count == 0:
96-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Entry not found.")
81+
await safe_delete_one(
82+
ENV_MANAGER_ENTRIES, {"_id": entry_id, "created_by": uid}, name="Entry"
83+
)

apps/backend/app/api/routes/notes/api.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from typing import Optional
21

32
from fastapi import APIRouter, Depends, Query
43

54
from app.api.routes.auth.services import get_current_uid
6-
from app.api.routes.notes.schema import NoteCreate, NoteOut, NoteUpdate
75
from app.api.routes.notes import services as note_svc
8-
6+
from app.api.routes.notes.schema import NoteCreate, NoteOut, NoteUpdate
97

108
router = APIRouter(prefix="/notes", tags=["notes"])
119

apps/backend/app/api/routes/notes/schema.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import json
2-
from typing import Any, Optional
2+
from typing import Any
33

44
from pydantic import BaseModel, ConfigDict, Field, field_validator
55

@@ -25,12 +25,12 @@ def _validate_content(value: Any) -> Any:
2525
class NoteCreate(BaseModel):
2626
model_config = ConfigDict(extra="ignore")
2727

28-
title: Optional[str] = Field(default=None, min_length=1, max_length=_TITLE_MAX_LEN)
28+
title: str | None = Field(default=None, min_length=1, max_length=_TITLE_MAX_LEN)
2929
content: Any = Field(default_factory=dict)
30-
parentId: Optional[str] = Field(default=None, max_length=128)
31-
icon: Optional[str] = Field(default=None, max_length=_ICON_MAX_LEN)
32-
pinned: Optional[bool] = None
33-
tags: Optional[list[str]] = None
30+
parentId: str | None = Field(default=None, max_length=128)
31+
icon: str | None = Field(default=None, max_length=_ICON_MAX_LEN)
32+
pinned: bool | None = None
33+
tags: list[str] | None = None
3434

3535
@field_validator("content", mode="before")
3636
@classmethod
@@ -52,12 +52,12 @@ def validate_tags(cls, v: Any) -> Any:
5252
class NoteUpdate(BaseModel):
5353
model_config = ConfigDict(extra="ignore")
5454

55-
title: Optional[str] = Field(default=None, min_length=1, max_length=_TITLE_MAX_LEN)
56-
content: Optional[Any] = None
57-
parentId: Optional[str] = Field(default=None, max_length=128)
58-
icon: Optional[str] = Field(default=None, max_length=_ICON_MAX_LEN)
59-
pinned: Optional[bool] = None
60-
tags: Optional[list[str]] = None
55+
title: str | None = Field(default=None, min_length=1, max_length=_TITLE_MAX_LEN)
56+
content: Any | None = None
57+
parentId: str | None = Field(default=None, max_length=128)
58+
icon: str | None = Field(default=None, max_length=_ICON_MAX_LEN)
59+
pinned: bool | None = None
60+
tags: list[str] | None = None
6161

6262
@field_validator("content", mode="before")
6363
@classmethod
@@ -82,8 +82,8 @@ class NoteOut(BaseModel):
8282
id: str
8383
title: str
8484
content: Any
85-
parentId: Optional[str] = None
86-
icon: Optional[str] = None
85+
parentId: str | None = None
86+
icon: str | None = None
8787
pinned: bool = False
8888
tags: list[str] = Field(default_factory=list)
8989
userId: str

apps/backend/app/api/routes/notes/services.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from datetime import datetime, timezone
2-
from typing import Any, Optional
2+
from typing import Any
33

44
from fastapi import HTTPException, status
5-
from pymongo.errors import PyMongoError
65

76
from app.api.routes.notes.schema import NoteCreate, NoteOut, NoteUpdate
8-
from app.core.cache import cached, bump_version
7+
from app.core.cache import bump_version, cached
8+
from app.database import db_manager
99
from app.utils.collection_name import NOTES
10+
from app.utils.crud import safe_insert, safe_update_one
1011
from app.utils.utils import new_id
11-
from app.database import db_manager
1212

1313

1414
def isoformat_utc(dt: datetime) -> str:
@@ -85,10 +85,7 @@ async def create_note(uid: str, body: NoteCreate) -> NoteOut:
8585
"createdAt": ts,
8686
"updatedAt": ts,
8787
}
88-
try:
89-
await db_manager.insert_one(NOTES, doc)
90-
except PyMongoError as exc:
91-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to create note.") from exc
88+
await safe_insert(NOTES, doc, name="Note")
9289
await bump_version(ns="notes", uid=uid)
9390
return _doc_to_out(doc)
9491

@@ -107,17 +104,9 @@ async def update_note(uid: str, note_id: str, body: NoteUpdate) -> NoteOut:
107104
return await get_note(uid=uid, note_id=note_id)
108105

109106
patch["updatedAt"] = datetime.now(timezone.utc)
110-
try:
111-
result = await db_manager.update_one(NOTES, {"_id": note_id, "created_by": uid}, {"$set": patch})
112-
except PyMongoError as exc:
113-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to update note.") from exc
114-
115-
if result.matched_count == 0:
116-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found.")
117-
118-
doc = await db_manager.find_one(NOTES, {"_id": note_id, "created_by": uid})
119-
if not doc:
120-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Note not found.")
107+
doc = await safe_update_one(
108+
NOTES, {"_id": note_id, "created_by": uid}, patch, name="Note"
109+
)
121110
await bump_version(ns="notes", uid=uid)
122111
return _doc_to_out(doc)
123112

0 commit comments

Comments
 (0)