adobe-to-docusign-migrator/web/routers/verify.py

132 lines
3.9 KiB
Python

"""
web/routers/verify.py
---------------------
Verification endpoints: send test envelopes, poll status, void.
Uses DocuSign Envelopes API to confirm migrated templates work end-to-end.
"""
from typing import Optional
import httpx
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from web.config import settings
from web.session import get_session
router = APIRouter()
class SendRequest(BaseModel):
template_id: str
recipient_name: str
recipient_email: str
class VoidRequest(BaseModel):
reason: str = "Test envelope — voided after verification"
def _require_docusign(session: dict) -> Optional[JSONResponse]:
if not session.get("docusign_access_token"):
return JSONResponse({"error": "not authenticated to DocuSign"}, status_code=401)
return None
@router.post("/send")
async def send_test_envelope(body: SendRequest, request: Request):
"""Send a test envelope using a migrated DocuSign template."""
session = get_session(request)
err = _require_docusign(session)
if err:
return err
payload = {
"templateId": body.template_id,
"status": "sent",
"templateRoles": [
{
"email": body.recipient_email,
"name": body.recipient_name,
"roleName": "Signer",
}
],
"emailSubject": f"[Verification Test] Please sign this document",
}
async with httpx.AsyncClient() as client:
resp = await client.post(
f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes",
headers={
"Authorization": f"Bearer {session['docusign_access_token']}",
"Content-Type": "application/json",
},
json=payload,
)
if not resp.is_success:
return JSONResponse(
{"error": "DocuSign API error", "detail": resp.text},
status_code=502,
)
data = resp.json()
return {"envelope_id": data.get("envelopeId")}
@router.get("/status/{envelope_id}")
async def envelope_status(envelope_id: str, request: Request):
"""Get the current status of a test envelope."""
session = get_session(request)
err = _require_docusign(session)
if err:
return err
async with httpx.AsyncClient() as client:
resp = await client.get(
f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes/{envelope_id}",
headers={"Authorization": f"Bearer {session['docusign_access_token']}"},
)
if not resp.is_success:
return JSONResponse(
{"error": "DocuSign API error", "detail": resp.text},
status_code=502,
)
data = resp.json()
return {
"envelope_id": envelope_id,
"status": data.get("status"),
"completed_at": data.get("completedDateTime"),
"sent_at": data.get("sentDateTime"),
}
@router.post("/void/{envelope_id}")
async def void_envelope(envelope_id: str, body: VoidRequest, request: Request):
"""Void a test envelope after verification is complete."""
session = get_session(request)
err = _require_docusign(session)
if err:
return err
async with httpx.AsyncClient() as client:
resp = await client.put(
f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/envelopes/{envelope_id}",
headers={
"Authorization": f"Bearer {session['docusign_access_token']}",
"Content-Type": "application/json",
},
json={"status": "voided", "voidedReason": body.reason},
)
if not resp.is_success:
return JSONResponse(
{"error": "DocuSign API error", "detail": resp.text},
status_code=502,
)
return {"voided": True, "envelope_id": envelope_id}