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

168 lines
5.5 KiB
Python

"""
web/routers/templates.py
------------------------
Template listing endpoints for Adobe Sign and DocuSign.
Computes per-template migration status for the side-by-side UI.
"""
from datetime import datetime, timezone
from typing import Optional
import httpx
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from web.config import settings
from web.session import get_session
router = APIRouter()
def _require_adobe(session: dict) -> Optional[JSONResponse]:
if not session.get("adobe_access_token"):
return JSONResponse({"error": "not authenticated to Adobe Sign"}, status_code=401)
return None
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.get("/adobe")
async def list_adobe_templates(request: Request):
"""List all Adobe Sign library documents (templates) for the current user."""
session = get_session(request)
err = _require_adobe(session)
if err:
return err
async with httpx.AsyncClient() as client:
resp = await client.get(
f"{settings.adobe_sign_base_url}/libraryDocuments",
headers={"Authorization": f"Bearer {session['adobe_access_token']}"},
params={"pageSize": 100},
)
if not resp.is_success:
return JSONResponse({"error": "Adobe Sign API error", "detail": resp.text}, status_code=502)
data = resp.json()
templates = [
{
"id": t.get("id"),
"name": t.get("name"),
"modifiedDate": t.get("modifiedDate"),
"sharingMode": t.get("sharingMode"),
}
for t in data.get("libraryDocumentList", [])
]
return {"templates": templates}
@router.get("/docusign")
async def list_docusign_templates(request: Request):
"""List all DocuSign templates for the account."""
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}/templates",
headers={"Authorization": f"Bearer {session['docusign_access_token']}"},
params={"count": 100},
)
if not resp.is_success:
return JSONResponse({"error": "DocuSign API error", "detail": resp.text}, status_code=502)
data = resp.json()
raw = data.get("envelopeTemplates") or data.get("templates") or []
templates = [
{
"id": t.get("templateId"),
"name": t.get("name"),
"lastModified": t.get("lastModified"),
}
for t in raw
]
return {"templates": templates}
@router.get("/status")
async def template_status(request: Request):
"""
Merged view: each Adobe template tagged with migration status.
Status values:
not_migrated — no DocuSign template with the same name
migrated — at least one exact name match in DocuSign
needs_update — name match exists but Adobe template is newer
"""
session = get_session(request)
err = _require_adobe(session) or _require_docusign(session)
if err:
return err
# Fetch both lists concurrently
async with httpx.AsyncClient() as client:
adobe_resp, ds_resp = await asyncio.gather(
client.get(
f"{settings.adobe_sign_base_url}/libraryDocuments",
headers={"Authorization": f"Bearer {session['adobe_access_token']}"},
params={"pageSize": 100},
),
client.get(
f"{settings.docusign_base_url}/v2.1/accounts/{settings.docusign_account_id}/templates",
headers={"Authorization": f"Bearer {session['docusign_access_token']}"},
params={"count": 100},
),
)
if not adobe_resp.is_success:
return JSONResponse({"error": "Adobe Sign API error"}, status_code=502)
if not ds_resp.is_success:
return JSONResponse({"error": "DocuSign API error"}, status_code=502)
adobe_templates = adobe_resp.json().get("libraryDocumentList", [])
ds_raw = ds_resp.json().get("envelopeTemplates") or ds_resp.json().get("templates") or []
# Build a name → most-recently-modified DocuSign template lookup
ds_by_name: dict[str, dict] = {}
for t in ds_raw:
name = t.get("name", "")
existing = ds_by_name.get(name)
if not existing or t.get("lastModified", "") > existing.get("lastModified", ""):
ds_by_name[name] = t
results = []
for t in adobe_templates:
name = t.get("name", "")
adobe_modified = t.get("modifiedDate", "")
ds_match = ds_by_name.get(name)
if not ds_match:
status = "not_migrated"
else:
ds_modified = ds_match.get("lastModified", "")
# needs_update if Adobe was modified after the DS template
status = "needs_update" if adobe_modified > ds_modified else "migrated"
results.append({
"adobe_id": t.get("id"),
"name": name,
"adobe_modified": adobe_modified,
"docusign_id": ds_match.get("templateId") if ds_match else None,
"docusign_modified": ds_match.get("lastModified") if ds_match else None,
"status": status,
})
return {"templates": results}
# asyncio needed for gather — import at top of module
import asyncio