fix(tickets): fix statuses endpoint, members auth gate, and graceful error handling
All checks were successful
Mirror to GitHub / mirror (push) Successful in 3s
All checks were successful
Mirror to GitHub / mirror (push) Successful in 3s
- Add GET /boards/{board_id}/statuses endpoint — direct board-to-statuses lookup
without ticket roundabout; used by filter bar and new ticket form
- Fix TicketsPage and NewTicketModal to call getBoardStatuses(board_id) instead
of misusing getTicketStatuses(ticket_id) with a board_id value
- Fix list_members auth: was require_account_owner (owner/super_admin only) —
changed to require_engineer_or_admin so engineers can see member list for
ticket assignment
- list_members: return [] on PSAError instead of 502 (Lesson 111 pattern)
- get_ticket_statuses: return [] on PSAError instead of 502
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -788,7 +788,30 @@ async def get_ticket_statuses(
|
||||
except PSANotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Ticket not found")
|
||||
except PSAError as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
logger.warning("get_ticket_statuses(%s) failed: %s", ticket_id, e)
|
||||
return []
|
||||
|
||||
|
||||
@router.get("/boards/{board_id}/statuses", response_model=list[PSATicketStatusItem])
|
||||
async def get_board_statuses(
|
||||
board_id: int,
|
||||
current_user: Annotated[User, Depends(require_engineer_or_admin)],
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
):
|
||||
"""Get available statuses for a service board directly (no ticket lookup required)."""
|
||||
if not current_user.account_id:
|
||||
raise HTTPException(status_code=400, detail="User has no account")
|
||||
|
||||
from app.services.psa.registry import get_provider_for_account
|
||||
from app.services.psa.exceptions import PSAError
|
||||
|
||||
try:
|
||||
provider = await get_provider_for_account(current_user.account_id, db)
|
||||
statuses = await provider.get_ticket_statuses(board_id)
|
||||
return [PSATicketStatusItem(id=s.id, name=s.name, is_closed=s.is_closed) for s in statuses]
|
||||
except PSAError as e:
|
||||
logger.warning("get_board_statuses(%s) failed: %s", board_id, e)
|
||||
return []
|
||||
|
||||
|
||||
# ── member mapping endpoints ─────────────────────────────────────────
|
||||
@@ -796,7 +819,7 @@ async def get_ticket_statuses(
|
||||
|
||||
@router.get("/members", response_model=list[PsaMemberResponse])
|
||||
async def list_members(
|
||||
current_user: Annotated[User, Depends(require_account_owner)],
|
||||
current_user: Annotated[User, Depends(require_engineer_or_admin)],
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
):
|
||||
"""List CW members (from CW API)."""
|
||||
@@ -814,7 +837,9 @@ async def list_members(
|
||||
for m in members
|
||||
]
|
||||
except PSAError as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
# Members are optional display data — degrade gracefully
|
||||
logger.warning("list_members failed: %s", e)
|
||||
return []
|
||||
|
||||
|
||||
@router.get("/member-mappings", response_model=list[PsaMemberMappingResponse])
|
||||
|
||||
Reference in New Issue
Block a user