Compare commits
4 Commits
d5043100a7
...
015e553d06
| Author | SHA1 | Date | |
|---|---|---|---|
| 015e553d06 | |||
| 6bdf9786ac | |||
| d87f9c5a5f | |||
| a0fab1f6de |
32
web/app.py
32
web/app.py
@@ -20,7 +20,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "mcp-server" / "
|
|||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
|
from fastapi import BackgroundTasks, FastAPI, File, Form, HTTPException, UploadFile
|
||||||
from fastapi.responses import FileResponse, StreamingResponse
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -44,7 +44,7 @@ from web.mcp_env_catalog import (
|
|||||||
normalize_for_compare,
|
normalize_for_compare,
|
||||||
)
|
)
|
||||||
from web.progress_store import ProgressStore
|
from web.progress_store import ProgressStore
|
||||||
from web.paperclip_api import pc_request
|
from web.paperclip_api import emit_case_status_webhook, pc_request
|
||||||
from web.paperclip_client import (
|
from web.paperclip_client import (
|
||||||
COMPANIES as PAPERCLIP_COMPANIES,
|
COMPANIES as PAPERCLIP_COMPANIES,
|
||||||
accept_interaction as pc_accept_interaction,
|
accept_interaction as pc_accept_interaction,
|
||||||
@@ -1337,8 +1337,12 @@ async def api_case_get(case_number: str):
|
|||||||
|
|
||||||
|
|
||||||
@app.put("/api/cases/{case_number}")
|
@app.put("/api/cases/{case_number}")
|
||||||
async def api_case_update(case_number: str, req: CaseUpdateRequest):
|
async def api_case_update(case_number: str, req: CaseUpdateRequest, background_tasks: BackgroundTasks):
|
||||||
"""Update case details."""
|
"""Update case details."""
|
||||||
|
# Capture old status before the update so we can detect changes.
|
||||||
|
existing = await db.get_case_by_number(case_number)
|
||||||
|
old_status = (existing or {}).get("status", "")
|
||||||
|
|
||||||
result = await cases_tools.case_update(
|
result = await cases_tools.case_update(
|
||||||
case_number=case_number,
|
case_number=case_number,
|
||||||
status=req.status,
|
status=req.status,
|
||||||
@@ -1351,10 +1355,30 @@ async def api_case_update(case_number: str, req: CaseUpdateRequest):
|
|||||||
expected_outcome=req.expected_outcome,
|
expected_outcome=req.expected_outcome,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return json.loads(result)
|
parsed = json.loads(result)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
raise HTTPException(404, result)
|
raise HTTPException(404, result)
|
||||||
|
|
||||||
|
# Emit webhook when status changes (fire-and-forget via BackgroundTasks).
|
||||||
|
new_status = req.status
|
||||||
|
if new_status and old_status != new_status:
|
||||||
|
prefix = case_number[:1]
|
||||||
|
company_id = (
|
||||||
|
PAPERCLIP_COMPANIES["licensing"] if prefix == "1"
|
||||||
|
else PAPERCLIP_COMPANIES["betterment"] if prefix in ("8", "9")
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
background_tasks.add_task(
|
||||||
|
emit_case_status_webhook,
|
||||||
|
case_number=case_number,
|
||||||
|
old_status=old_status,
|
||||||
|
new_status=new_status,
|
||||||
|
company_id=company_id, # None is safe — plugin handles unknown company gracefully
|
||||||
|
)
|
||||||
|
logger.debug("webhook scheduled: case %s %s → %s", case_number, old_status, new_status)
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/api/cases")
|
@app.delete("/api/cases")
|
||||||
async def api_case_delete(case_number: str, remove_files: bool = False):
|
async def api_case_delete(case_number: str, remove_files: bool = False):
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@@ -81,3 +82,35 @@ async def pc_request(
|
|||||||
if raise_on_error:
|
if raise_on_error:
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
async def emit_case_status_webhook(
|
||||||
|
case_number: str,
|
||||||
|
old_status: str,
|
||||||
|
new_status: str,
|
||||||
|
company_id: str | None = None,
|
||||||
|
run_id: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Notify the Paperclip plugin that a case status changed.
|
||||||
|
|
||||||
|
Fire-and-forget: logs errors but never raises, so callers aren't blocked.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
await pc_request(
|
||||||
|
"POST",
|
||||||
|
"/api/plugins/marcusgroup.legal-ai/webhooks/case-status",
|
||||||
|
json={
|
||||||
|
"caseNumber": case_number,
|
||||||
|
"oldStatus": old_status,
|
||||||
|
"newStatus": new_status,
|
||||||
|
"companyId": company_id,
|
||||||
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||||
|
},
|
||||||
|
run_id=run_id,
|
||||||
|
timeout=5.0,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(
|
||||||
|
"emit_case_status_webhook failed for case %s (%s → %s): %s",
|
||||||
|
case_number, old_status, new_status, exc,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user