Skip to content

Commit 67f2102

Browse files
Annotate FastAPI routes for better OpenAPI documentation
1 parent 8bfa36e commit 67f2102

5 files changed

Lines changed: 40 additions & 37 deletions

File tree

routers/core/account.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161

6262

6363
def validate_password_strength_and_match(
64-
password: str = Form(...),
65-
confirm_password: str = Form(...)
64+
password: str = Form(..., title="Password", description="Account password"),
65+
confirm_password: str = Form(..., title="Confirm password", description="Re-enter password to confirm")
6666
) -> str:
6767
"""
6868
Validates password strength and confirms passwords match.
@@ -120,9 +120,9 @@ async def read_login(
120120
if user:
121121
return RedirectResponse(url=dashboard_router.url_path_for("read_dashboard"), status_code=302)
122122
return templates.TemplateResponse(
123+
request,
123124
"account/login.html",
124125
{
125-
"request": request,
126126
"user": user,
127127
"invitation_token": invitation_token
128128
}
@@ -143,9 +143,9 @@ async def read_register(
143143
return RedirectResponse(url=dashboard_router.url_path_for("read_dashboard"), status_code=302)
144144

145145
return templates.TemplateResponse(
146+
request,
146147
"account/register.html",
147148
{
148-
"request": request,
149149
"user": user,
150150
"password_pattern": HTML_PASSWORD_PATTERN,
151151
"email": email,
@@ -167,8 +167,9 @@ async def read_forgot_password(
167167
return RedirectResponse(url=dashboard_router.url_path_for("read_dashboard"), status_code=302)
168168

169169
return templates.TemplateResponse(
170+
request,
170171
"account/forgot_password.html",
171-
{"request": request, "user": user, "show_form": show_form == "true"}
172+
{"user": user, "show_form": show_form == "true"}
172173
)
173174

174175

@@ -190,15 +191,16 @@ async def read_reset_password(
190191
raise CredentialsError(message="Invalid or expired token")
191192

192193
return templates.TemplateResponse(
194+
request,
193195
"account/reset_password.html",
194-
{"request": request, "user": user, "email": email, "token": token, "password_pattern": HTML_PASSWORD_PATTERN}
196+
{"user": user, "email": email, "token": token, "password_pattern": HTML_PASSWORD_PATTERN}
195197
)
196198

197199

198200
@router.post("/delete", response_class=RedirectResponse)
199201
async def delete_account(
200-
email: EmailStr = Form(...),
201-
password: str = Form(...),
202+
email: EmailStr = Form(..., title="Email", description="Account email address for verification"),
203+
password: str = Form(..., title="Password", description="Account password for verification"),
202204
account: Account = Depends(get_authenticated_account),
203205
session: Session = Depends(get_session)
204206
):
@@ -229,12 +231,12 @@ async def delete_account(
229231
async def register(
230232
request: Request,
231233
_ip_check: None = Depends(check_register_ip_rate_limit),
232-
name: str = Form(...),
233-
email: EmailStr = Form(...),
234+
name: str = Form(..., min_length=1, strip_whitespace=True, title="Name", description="Your full name"),
235+
email: EmailStr = Form(..., title="Email", description="Email address for the new account"),
234236
session: Session = Depends(get_session),
235237
_: None = Depends(validate_password_strength_and_match),
236-
password: str = Form(...),
237-
invitation_token: Optional[str] = Form(None)
238+
password: str = Form(..., title="Password", description="Account password"),
239+
invitation_token: Optional[str] = Form(None, title="Invitation token", description="Optional invitation token to join an organization")
238240
) -> Response:
239241
"""
240242
Register a new user account, optionally processing an invitation.
@@ -353,7 +355,7 @@ async def login(
353355
_ip_check: None = Depends(check_login_ip_rate_limit),
354356
_email_check: EmailStr = Depends(check_login_email_rate_limit),
355357
account_and_session: Tuple[Account, Session] = Depends(get_account_from_credentials),
356-
invitation_token: Optional[str] = Form(None)
358+
invitation_token: Optional[str] = Form(None, title="Invitation token", description="Optional invitation token to join an organization after login")
357359
) -> Response:
358360
"""
359361
Log in a user with valid credentials and process invitation if token is provided.
@@ -534,8 +536,8 @@ async def forgot_password(
534536
@router.post("/reset_password")
535537
async def reset_password(
536538
request: Request,
537-
email: EmailStr = Form(...),
538-
token: str = Form(...),
539+
email: EmailStr = Form(..., title="Email", description="Account email address"),
540+
token: str = Form(..., title="Reset token", description="Password reset token from email"),
539541
new_password: str = Depends(validate_password_strength_and_match),
540542
session: Session = Depends(get_session)
541543
):
@@ -569,8 +571,8 @@ async def reset_password(
569571
@router.post("/update_email")
570572
async def request_email_update(
571573
request: Request,
572-
email: EmailStr = Form(...),
573-
new_email: EmailStr = Form(...),
574+
email: EmailStr = Form(..., title="Current email", description="Current account email address"),
575+
new_email: EmailStr = Form(..., title="New email", description="New email address to update to"),
574576
account: Account = Depends(get_authenticated_account),
575577
session: Session = Depends(get_session)
576578
):

routers/core/dashboard.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ async def read_dashboard(
1717
user: Optional[User] = Depends(get_user_with_relations)
1818
):
1919
return templates.TemplateResponse(
20+
request,
2021
"dashboard/index.html",
21-
{"request": request, "user": user}
22+
{"user": user}
2223
)

routers/core/invitation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ async def create_invitation(
5454
request: Request,
5555
current_user: User = Depends(get_authenticated_user),
5656
session: Session = Depends(get_session),
57-
invitee_email: EmailStr = Form(...),
58-
role_id: int = Form(...),
59-
organization_id: int = Form(...),
57+
invitee_email: EmailStr = Form(..., title="Invitee email", description="Email address of the person to invite"),
58+
role_id: int = Form(..., title="Role ID", description="ID of the role to assign to the invitee"),
59+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization to invite the user to"),
6060
):
6161
# Fetch the organization
6262
organization = session.get(Organization, organization_id)

routers/core/role.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# TODO: User with permission to create/edit roles can only assign permissions
22
# they themselves have.
3-
from typing import List, Sequence, Optional
3+
from typing import Annotated, List, Sequence, Optional
44
from logging import getLogger
55
from fastapi import APIRouter, Depends, Form, Request
66
from fastapi.responses import RedirectResponse
@@ -43,9 +43,9 @@ def _load_org_for_roles_partial(session: Session, organization_id: int, user: Us
4343
@router.post("/create", response_class=RedirectResponse)
4444
def create_role(
4545
request: Request,
46-
name: str = Form(...),
47-
organization_id: int = Form(...),
48-
permissions: List[ValidPermissions] = Form(default=[]),
46+
name: Annotated[str, Form(min_length=1, strip_whitespace=True, title="Role name", description="Name for the new role")],
47+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization this role belongs to"),
48+
permissions: List[ValidPermissions] = Form(default=[], title="Permissions", description="List of permissions to assign to this role"),
4949
user: User = Depends(get_authenticated_user),
5050
session: Session = Depends(get_session)
5151
):
@@ -107,10 +107,10 @@ def create_role(
107107
@router.post("/update", response_class=RedirectResponse)
108108
def update_role(
109109
request: Request,
110-
id: int = Form(...),
111-
name: str = Form(...),
112-
organization_id: int = Form(...),
113-
permissions: List[ValidPermissions] = Form(default=[]),
110+
id: int = Form(..., title="Role ID", description="ID of the role to update"),
111+
name: str = Form(..., min_length=1, strip_whitespace=True, title="Role name", description="Updated name for the role"),
112+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization this role belongs to"),
113+
permissions: List[ValidPermissions] = Form(default=[], title="Permissions", description="Updated list of permissions for this role"),
114114
user: User = Depends(get_authenticated_user),
115115
session: Session = Depends(get_session)
116116
):
@@ -197,8 +197,8 @@ def update_role(
197197
@router.post("/delete", response_class=RedirectResponse)
198198
def delete_role(
199199
request: Request,
200-
id: int = Form(...),
201-
organization_id: int = Form(...),
200+
id: int = Form(..., title="Role ID", description="ID of the role to delete"),
201+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization this role belongs to"),
202202
user: User = Depends(get_authenticated_user),
203203
session: Session = Depends(get_session)
204204
):

routers/core/user.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async def read_profile(
6666
@router.post("/update", response_class=RedirectResponse)
6767
async def update_profile(
6868
request: Request,
69-
name: Optional[str] = Form(None),
69+
name: Optional[str] = Form(None, strip_whitespace=True, title="Name", description="Updated display name"),
7070
avatar_file: Optional[UploadFile] = File(None),
7171
user: User = Depends(get_authenticated_user),
7272
session: Session = Depends(get_session)
@@ -133,9 +133,9 @@ async def get_avatar(
133133
@router.post("/role/update", response_class=RedirectResponse)
134134
def update_user_role(
135135
request: Request,
136-
user_id: int = Form(...),
137-
organization_id: int = Form(...),
138-
roles: Optional[List[int]] = Form(None),
136+
user_id: int = Form(..., title="User ID", description="ID of the user whose roles are being updated"),
137+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization"),
138+
roles: Optional[List[int]] = Form(None, title="Role IDs", description="List of role IDs to assign to the user"),
139139
user: User = Depends(get_authenticated_user),
140140
session: Session = Depends(get_session)
141141
) -> Response:
@@ -205,8 +205,8 @@ def update_user_role(
205205
@router.post("/organization/remove", response_class=RedirectResponse)
206206
def remove_user_from_organization(
207207
request: Request,
208-
user_id: int = Form(...),
209-
organization_id: int = Form(...),
208+
user_id: int = Form(..., title="User ID", description="ID of the user to remove"),
209+
organization_id: int = Form(..., title="Organization ID", description="ID of the organization to remove the user from"),
210210
user: User = Depends(get_authenticated_user),
211211
session: Session = Depends(get_session)
212212
) -> Response:

0 commit comments

Comments
 (0)