- Add complete Python backend (Litestar + LangGraph) with chat, conversations, tickets APIs - Add GitHub Actions workflow for auto-deploying backend to Azure Web App (soc-backend) - Add gunicorn to requirements.txt for production serving - Update CLAUDE.md and EXTERNAL_SERVICES.md with latest config - Remove obsolete claudehd.md (merged into gpthd.md) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
81 lines
2.4 KiB
Python
81 lines
2.4 KiB
Python
"""Ticket API endpoints — proxy to Gongdan system.
|
|
|
|
Returns data in a format aligned with the frontend TicketData interface:
|
|
{ id, title, status, priority, createdAt }
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
from litestar import get
|
|
from litestar.exceptions import NotFoundException
|
|
|
|
from app.config import settings
|
|
|
|
|
|
def _gongdan_headers() -> dict[str, str]:
|
|
return {"X-Api-Key": settings.gongdan_api_key}
|
|
|
|
|
|
def _map_status(raw: str) -> str:
|
|
mapping = {
|
|
"OPEN": "pending",
|
|
"ASSIGNED": "processing",
|
|
"IN_PROGRESS": "processing",
|
|
"PENDING_CUSTOMER": "processing",
|
|
"RESOLVED": "resolved",
|
|
"CLOSED": "resolved",
|
|
}
|
|
return mapping.get(raw, "pending")
|
|
|
|
|
|
def _map_priority(raw: str) -> str:
|
|
mapping = {
|
|
"URGENT": "P0",
|
|
"PRIORITY": "P1",
|
|
"NORMAL": "P2",
|
|
"LOW": "P3",
|
|
}
|
|
return mapping.get(raw, "P2")
|
|
|
|
|
|
def _transform_ticket(t: dict) -> dict:
|
|
"""Transform a Gongdan ticket to the frontend TicketData shape."""
|
|
return {
|
|
"id": t.get("ticketNumber", t.get("id", "")),
|
|
"title": t.get("description", "")[:120] or "No description",
|
|
"status": _map_status(t.get("status", "")),
|
|
"priority": _map_priority(t.get("priority", "")),
|
|
"createdAt": t.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
@get("/api/tickets")
|
|
async def list_tickets(page: int = 1, page_size: int = 20) -> list[dict]:
|
|
"""GET /api/tickets — List tickets from Gongdan, formatted for frontend."""
|
|
url = f"{settings.gongdan_api_base}/api/tickets"
|
|
params = {"page": page, "pageSize": page_size}
|
|
|
|
async with httpx.AsyncClient(timeout=15) as client:
|
|
resp = await client.get(url, params=params, headers=_gongdan_headers())
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
tickets = data.get("tickets", [])
|
|
return [_transform_ticket(t) for t in tickets]
|
|
|
|
|
|
@get("/api/tickets/{ticket_id:str}")
|
|
async def get_ticket(ticket_id: str) -> dict:
|
|
"""GET /api/tickets/:id — Get a single ticket detail."""
|
|
url = f"{settings.gongdan_api_base}/api/tickets/{ticket_id}"
|
|
|
|
async with httpx.AsyncClient(timeout=15) as client:
|
|
resp = await client.get(url, headers=_gongdan_headers())
|
|
if resp.status_code == 404:
|
|
raise NotFoundException(detail=f"Ticket {ticket_id} not found")
|
|
resp.raise_for_status()
|
|
t = resp.json()
|
|
|
|
return _transform_ticket(t)
|