feat: switch backend indexing to code-first identifiers

This commit is contained in:
2026-04-03 16:25:11 +00:00
parent 58d3ca6ad0
commit ae353afbed
10 changed files with 354 additions and 377 deletions

View File

@@ -20,7 +20,7 @@ from app.services.activity import log_activity
from app.services.dependency_check import check_milestone_deps
router = APIRouter(
prefix="/projects/{project_id}/milestones/{milestone_id}/actions",
prefix="/projects/{project_code}/milestones/{milestone_code}/actions",
tags=["Milestone Actions"],
)
@@ -29,10 +29,18 @@ router = APIRouter(
# Helpers
# ---------------------------------------------------------------------------
def _get_milestone_or_404(db: Session, project_id: int, milestone_id: int) -> Milestone:
def _resolve_project_or_404(db: Session, project_code: str):
project = db.query(models.Project).filter(models.Project.project_code == project_code).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found")
return project
def _get_milestone_or_404(db: Session, project_code: str, milestone_code: str) -> Milestone:
project = _resolve_project_or_404(db, project_code)
ms = (
db.query(Milestone)
.filter(Milestone.id == milestone_id, Milestone.project_id == project_id)
.filter(Milestone.milestone_code == milestone_code, Milestone.project_id == project.id)
.first()
)
if not ms:
@@ -59,8 +67,8 @@ class CloseBody(BaseModel):
@router.get("/preflight", status_code=200)
def preflight_milestone_actions(
project_id: int,
milestone_id: int,
project_code: str,
milestone_code: str,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user_or_apikey),
):
@@ -69,8 +77,9 @@ def preflight_milestone_actions(
The frontend uses this to decide whether to *disable* buttons and what
hint text to show. This endpoint never mutates data.
"""
check_project_role(db, current_user.id, project_id, min_role="viewer")
ms = _get_milestone_or_404(db, project_id, milestone_id)
project = _resolve_project_or_404(db, project_code)
check_project_role(db, current_user.id, project.id, min_role="viewer")
ms = _get_milestone_or_404(db, project_code, milestone_code)
ms_status = _ms_status_value(ms)
result: dict = {"status": ms_status, "freeze": None, "start": None}
@@ -80,7 +89,7 @@ def preflight_milestone_actions(
release_tasks = (
db.query(Task)
.filter(
Task.milestone_id == milestone_id,
Task.milestone_id == ms.id,
Task.task_type == "maintenance",
Task.task_subtype == "release",
)
@@ -118,8 +127,8 @@ def preflight_milestone_actions(
@router.post("/freeze", status_code=200)
def freeze_milestone(
project_id: int,
milestone_id: int,
project_code: str,
milestone_code: str,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user_or_apikey),
):
@@ -130,10 +139,11 @@ def freeze_milestone(
- Milestone must have **exactly one** maintenance task with subtype ``release``.
- Caller must have ``freeze milestone`` permission.
"""
check_project_role(db, current_user.id, project_id, min_role="mgr")
check_permission(db, current_user.id, project_id, "milestone.freeze")
project = _resolve_project_or_404(db, project_code)
check_project_role(db, current_user.id, project.id, min_role="mgr")
check_permission(db, current_user.id, project.id, "milestone.freeze")
ms = _get_milestone_or_404(db, project_id, milestone_id)
ms = _get_milestone_or_404(db, project_code, milestone_code)
if _ms_status_value(ms) != "open":
raise HTTPException(
@@ -145,7 +155,7 @@ def freeze_milestone(
release_tasks = (
db.query(Task)
.filter(
Task.milestone_id == milestone_id,
Task.milestone_id == ms.id,
Task.task_type == "maintenance",
Task.task_subtype == "release",
)
@@ -184,8 +194,8 @@ def freeze_milestone(
@router.post("/start", status_code=200)
def start_milestone(
project_id: int,
milestone_id: int,
project_code: str,
milestone_code: str,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user_or_apikey),
):
@@ -196,10 +206,11 @@ def start_milestone(
- All milestone dependencies must be completed.
- Caller must have ``start milestone`` permission.
"""
check_project_role(db, current_user.id, project_id, min_role="mgr")
check_permission(db, current_user.id, project_id, "milestone.start")
project = _resolve_project_or_404(db, project_code)
check_project_role(db, current_user.id, project.id, min_role="mgr")
check_permission(db, current_user.id, project.id, "milestone.start")
ms = _get_milestone_or_404(db, project_id, milestone_id)
ms = _get_milestone_or_404(db, project_code, milestone_code)
if _ms_status_value(ms) != "freeze":
raise HTTPException(
@@ -240,8 +251,8 @@ def start_milestone(
@router.post("/close", status_code=200)
def close_milestone(
project_id: int,
milestone_id: int,
project_code: str,
milestone_code: str,
body: CloseBody = CloseBody(),
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user_or_apikey),
@@ -252,10 +263,11 @@ def close_milestone(
- Milestone must be in ``open``, ``freeze``, or ``undergoing`` status.
- Caller must have ``close milestone`` permission.
"""
check_project_role(db, current_user.id, project_id, min_role="mgr")
check_permission(db, current_user.id, project_id, "milestone.close")
project = _resolve_project_or_404(db, project_code)
check_project_role(db, current_user.id, project.id, min_role="mgr")
check_permission(db, current_user.id, project.id, "milestone.close")
ms = _get_milestone_or_404(db, project_id, milestone_id)
ms = _get_milestone_or_404(db, project_code, milestone_code)
current = _ms_status_value(ms)
allowed_from = {"open", "freeze", "undergoing"}