From 15126aa0e5a2835334ca90f924f23c2cad786f2c Mon Sep 17 00:00:00 2001 From: zhi Date: Sun, 22 Mar 2026 10:40:13 +0000 Subject: [PATCH] Apply fix: accept project_code as identifier in project endpoints --- app/api/routers/projects.py | 46 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/app/api/routers/projects.py b/app/api/routers/projects.py index fa33080..8469939 100644 --- a/app/api/routers/projects.py +++ b/app/api/routers/projects.py @@ -15,6 +15,19 @@ from app.api.rbac import check_project_role, check_permission, ensure_can_edit_p router = APIRouter(prefix="/projects", tags=["Projects"]) +def _resolve_project(db: Session, identifier: str) -> models.Project: + """Resolve a project by numeric id or project_code string. + Raises 404 if not found.""" + try: + pid = int(identifier) + project = db.query(models.Project).filter(models.Project.id == pid).first() + except (ValueError, TypeError): + project = db.query(models.Project).filter(models.Project.project_code == identifier).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + return project + + def _validate_project_links(db, codes: list[str] | None, self_code: str | None = None) -> list[str] | None: if not codes: return None @@ -196,10 +209,7 @@ def _find_project_by_id_or_code(db, identifier) -> models.Project | None: @router.get("/{project_id}", response_model=schemas.ProjectResponse) def get_project(project_id: str, db: Session = Depends(get_db)): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") - return project + return _resolve_project(db, project_id) @router.patch("/{project_id}", response_model=schemas.ProjectResponse) @@ -209,9 +219,7 @@ def update_project( db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey), ): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") + project = _resolve_project(db, project_id) ensure_can_edit_project(db, current_user.id, project) update_data = project_update.model_dump(exclude_unset=True) update_data.pop("name", None) @@ -236,18 +244,16 @@ def delete_project( db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey), ): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") - + project = _resolve_project(db, project_id) check_project_role(db, current_user.id, project.id, min_role="admin") project_code = project.project_code + project_id_val = project.id # Delete milestones and their tasks from app.models.milestone import Milestone from app.models.task import Task - milestones = db.query(Milestone).filter(Milestone.project_id == project.id).all() + milestones = db.query(Milestone).filter(Milestone.project_id == project_id_val).all() for ms in milestones: tasks = db.query(Task).filter(Task.milestone_id == ms.id).all() for task in tasks: @@ -287,9 +293,7 @@ def add_project_member( db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey), ): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") + project = _resolve_project(db, project_id) check_project_role(db, current_user.id, project.id, min_role="mgr") user = db.query(models.User).filter(models.User.id == member.user_id).first() if not user: @@ -321,9 +325,7 @@ def add_project_member( @router.get("/{project_id}/members", response_model=List[schemas.ProjectMemberResponse]) def list_project_members(project_id: str, db: Session = Depends(get_db)): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") + project = _resolve_project(db, project_id) members = db.query(models.ProjectMember).filter(models.ProjectMember.project_id == project.id).all() result = [] for m in members: @@ -351,9 +353,7 @@ def remove_project_member( db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey), ): - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") + project = _resolve_project(db, project_id) check_permission(db, current_user.id, project.id, "member.remove") member = db.query(models.ProjectMember).filter( models.ProjectMember.project_id == project.id, models.ProjectMember.user_id == user_id @@ -386,9 +386,7 @@ from sqlalchemy import func as sqlfunc @router.get("/{project_id}/worklogs/summary") def project_worklog_summary(project_id: str, db: Session = Depends(get_db)): from app.models.task import Task as TaskModel - project = _find_project_by_id_or_code(db, project_id) - if not project: - raise HTTPException(status_code=404, detail="Project not found") + project = _resolve_project(db, project_id) resolved_project_id = project.id results = db.query( models.User.id, models.User.username,