feat: add GET /integrations/psa/tickets/{id}/context endpoint (Task 9)
Returns rich TicketContext for a ticket ID. Handles PSA auth failures (returns structured error), ticket-not-found (404), and general PSA errors (502). Requires active PSA connection for the user's account. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -319,6 +319,61 @@ async def search_tickets(
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/tickets/{ticket_id}/context")
|
||||
async def get_ticket_context(
|
||||
ticket_id: int,
|
||||
current_user: Annotated[User, Depends(get_current_active_user)],
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
):
|
||||
"""Get rich ticket context (company, contact, configs, notes, related tickets) for AI prompt injection."""
|
||||
from app.services.psa.registry import get_provider_for_account
|
||||
from app.services.psa.exceptions import (
|
||||
PSAError,
|
||||
PSAAuthError,
|
||||
PSAPermissionError,
|
||||
PSANotFoundError,
|
||||
PSAConnectionError,
|
||||
)
|
||||
from app.schemas.psa_context import TicketContext
|
||||
|
||||
if not current_user.account_id:
|
||||
raise HTTPException(status_code=400, detail="User has no account")
|
||||
|
||||
# Look up the active connection for connection_id
|
||||
conn_result = await db.execute(
|
||||
select(PsaConnection).where(
|
||||
PsaConnection.account_id == current_user.account_id,
|
||||
PsaConnection.is_active.is_(True),
|
||||
)
|
||||
)
|
||||
connection = conn_result.scalar_one_or_none()
|
||||
if not connection:
|
||||
raise HTTPException(status_code=404, detail="No active PSA connection configured")
|
||||
|
||||
try:
|
||||
provider = await get_provider_for_account(current_user.account_id, db)
|
||||
except PSAConnectionError:
|
||||
raise HTTPException(status_code=404, detail="No active PSA connection configured")
|
||||
except PSAError as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
|
||||
try:
|
||||
ctx: TicketContext = await provider.get_ticket_context(
|
||||
ticket_id=ticket_id,
|
||||
connection_id=str(connection.id),
|
||||
)
|
||||
return ctx
|
||||
except (PSAAuthError, PSAPermissionError):
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail={"error": "psa_auth_failed", "message": "PSA credentials may have expired."},
|
||||
)
|
||||
except PSANotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Ticket not found")
|
||||
except PSAError as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/tickets/{ticket_id}")
|
||||
async def get_ticket(
|
||||
ticket_id: str,
|
||||
|
||||
Reference in New Issue
Block a user