fix(sprints): move velocity endpoint before {sprint_id} routes

FastAPI processes routes in order, so /velocity must be defined
before /{sprint_id} to prevent "velocity" from being parsed as a UUID.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-31 13:19:37 +01:00
parent f0b04d53af
commit 81e8d7e73d

View File

@@ -384,6 +384,68 @@ async def get_active_sprint(
raise
@router.get(
"/velocity",
response_model=list[SprintVelocity],
summary="Get Project Velocity",
description="""
Get velocity metrics for completed sprints in the project.
**Authentication**: Required (Bearer token)
**Authorization**: Project owner or superuser
Returns velocity data for the last N completed sprints (default 5).
Useful for capacity planning and sprint estimation.
**Rate Limit**: 60 requests/minute
""",
operation_id="get_project_velocity",
)
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
async def get_project_velocity(
request: Request,
project_id: UUID,
limit: int = Query(
default=5,
ge=1,
le=20,
description="Number of completed sprints to include",
),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> Any:
"""
Get velocity metrics for completed sprints.
Returns planned points, actual velocity, and velocity ratio
for the last N completed sprints, ordered chronologically.
"""
# Verify project access
await verify_project_ownership(db, project_id, current_user)
try:
velocity_data = await sprint_crud.get_velocity(
db, project_id=project_id, limit=limit
)
return [
SprintVelocity(
sprint_number=item["sprint_number"],
sprint_name=item["sprint_name"],
planned_points=item["planned_points"],
velocity=item["velocity"],
velocity_ratio=item["velocity_ratio"],
)
for item in velocity_data
]
except Exception as e:
logger.error(
f"Error getting velocity for project {project_id}: {e!s}", exc_info=True
)
raise
@router.get(
"/{sprint_id}",
response_model=SprintResponse,
@@ -1116,70 +1178,3 @@ async def remove_issue_from_sprint(
exc_info=True,
)
raise
# ============================================================================
# Sprint Metrics Endpoints
# ============================================================================
@router.get(
"/velocity",
response_model=list[SprintVelocity],
summary="Get Project Velocity",
description="""
Get velocity metrics for completed sprints in the project.
**Authentication**: Required (Bearer token)
**Authorization**: Project owner or superuser
Returns velocity data for the last N completed sprints (default 5).
Useful for capacity planning and sprint estimation.
**Rate Limit**: 60 requests/minute
""",
operation_id="get_project_velocity",
)
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
async def get_project_velocity(
request: Request,
project_id: UUID,
limit: int = Query(
default=5,
ge=1,
le=20,
description="Number of completed sprints to include",
),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> Any:
"""
Get velocity metrics for completed sprints.
Returns planned points, actual velocity, and velocity ratio
for the last N completed sprints, ordered chronologically.
"""
# Verify project access
await verify_project_ownership(db, project_id, current_user)
try:
velocity_data = await sprint_crud.get_velocity(
db, project_id=project_id, limit=limit
)
return [
SprintVelocity(
sprint_number=item["sprint_number"],
sprint_name=item["sprint_name"],
planned_points=item["planned_points"],
velocity=item["velocity"],
velocity_ratio=item["velocity_ratio"],
)
for item in velocity_data
]
except Exception as e:
logger.error(
f"Error getting velocity for project {project_id}: {e!s}", exc_info=True
)
raise