fix: resolve latent bugs found in code review

- Fix ValueError crash in migrate_template.py and migrate_paul_template.py:
  compose_template() returns a 3-tuple since Phase 23 but both CLI scripts
  were still unpacking 2 values
- Fix ImportError in bulk-send/bulk_send.py: replace non-existent auth_helper
  import with docusign_auth.get_access_token via sys.path
- Activate log sanitizer at web app startup so tokens never appear in logs
- Log a warning at startup when SESSION_SECRET_KEY is the default dev value
- Add reportlab to requirements.txt (used by generate_pdfs.py, was missing)
- Move asyncio import from bottom of templates.py to top where it belongs
- Correct stale coordinate comment in generate_pdfs.py (both platforms use
  top-left origin; the comment incorrectly described bottom-left inversion)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Paul Huliganga 2026-04-23 09:51:20 -04:00
parent 210f273c05
commit c5b7b9f5b8
7 changed files with 28 additions and 10 deletions

View File

@ -13,12 +13,15 @@ DocuSign API reference:
""" """
import os import os
import sys
import csv import csv
import json import json
import argparse import argparse
import requests import requests
from dotenv import load_dotenv from dotenv import load_dotenv
from auth_helper import get_access_token # reuses existing JWT auth
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from docusign_auth import get_access_token
load_dotenv() load_dotenv()

View File

@ -10,6 +10,9 @@ uvicorn[standard]
itsdangerous itsdangerous
httpx httpx
# PDF generation (sample template tooling)
reportlab
# Testing # Testing
responses responses
respx respx

View File

@ -6,9 +6,9 @@ Generates realistic sample PDFs for adobe-to-docusign migration testing.
Each PDF mirrors the form fields described in the matching *-formfields.json Each PDF mirrors the form fields described in the matching *-formfields.json
so that tab positions map to visible labels on the document. so that tab positions map to visible labels on the document.
Adobe rect coordinates are top-left origin; DocuSign yPosition is bottom-left. Both Adobe Sign and DocuSign use top-left origin with y increasing downward no
Formula: docusign_y = PAGE_HEIGHT - adobe_top - adobe_height coordinate inversion is needed. DocuSign xPosition = adobe left, yPosition = adobe top.
To place a *label* just above a field: label_y = PAGE_HEIGHT - adobe_top + 2 To place a *label* just above a field in this PDF: label_y = page_height - adobe_top + 2
""" """
import base64 import base64

View File

@ -115,7 +115,7 @@ def run_migration(template_dir: Path) -> Path:
output_path = MIGRATION_OUTPUT_DIR / template_dir.name / "docusign-template.json" output_path = MIGRATION_OUTPUT_DIR / template_dir.name / "docusign-template.json"
print(f"\nRunning migration: {template_dir.name}") print(f"\nRunning migration: {template_dir.name}")
template_dict, warnings = compose_template(str(template_dir), str(output_path)) template_dict, warnings, field_issues = compose_template(str(template_dir), str(output_path))
print(f" Written: {output_path}") print(f" Written: {output_path}")
if warnings: if warnings:

View File

@ -121,7 +121,7 @@ def download_template(template) -> Path:
def convert_template(template_dir: Path) -> Path: def convert_template(template_dir: Path) -> Path:
output_path = OUTPUT_DIR / template_dir.name / "docusign-template.json" output_path = OUTPUT_DIR / template_dir.name / "docusign-template.json"
print(f"\nConverting to DocuSign format...") print(f"\nConverting to DocuSign format...")
_, warnings = compose_template(str(template_dir), str(output_path)) _, warnings, _ = compose_template(str(template_dir), str(output_path))
print(f" Written: {output_path}") print(f" Written: {output_path}")
for w in warnings: for w in warnings:
print(f" WARNING: {w}") print(f" WARNING: {w}")

View File

@ -12,11 +12,26 @@ From the project root.
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, HTMLResponse from fastapi.responses import FileResponse, HTMLResponse
import logging
import os import os
from web.config import settings from web.config import settings
from web.routers import auth, templates, migrate, verify, audit, admin from web.routers import auth, templates, migrate, verify, audit, admin
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from src.utils.log_sanitizer import install_sanitizing_filter
install_sanitizing_filter()
logger = logging.getLogger(__name__)
_DEFAULT_SECRET = "dev-secret-change-in-production"
if settings.session_secret_key == _DEFAULT_SECRET:
logger.warning(
"SESSION_SECRET_KEY is using the default dev value — set a random secret in .env before exposing this app"
)
app = FastAPI( app = FastAPI(
title="Adobe Sign → DocuSign Migrator", title="Adobe Sign → DocuSign Migrator",
version=settings.version, version=settings.version,

View File

@ -5,6 +5,7 @@ Template listing endpoints for Adobe Sign and DocuSign.
Computes per-template migration status for the side-by-side UI. Computes per-template migration status for the side-by-side UI.
""" """
import asyncio
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
import tempfile import tempfile
@ -243,7 +244,3 @@ def _dedupe(items: list[str]) -> list[str]:
seen.add(item) seen.add(item)
result.append(item) result.append(item)
return result return result
# asyncio needed for gather — import at top of module
import asyncio